Break out OpenSSL-specific code to separate files.
authorHeikki Linnakangas <[email protected]>
Mon, 11 Aug 2014 08:54:19 +0000 (11:54 +0300)
committerHeikki Linnakangas <[email protected]>
Mon, 11 Aug 2014 08:54:19 +0000 (11:54 +0300)
This refactoring is in preparation for adding support for other SSL
implementations, with no user-visible effects. There are now two #defines,
USE_OPENSSL which is defined when building with OpenSSL, and USE_SSL which
is defined when building with any SSL implementation. Currently, OpenSSL is
the only implementation so the two #defines go together, but USE_SSL is
supposed to be used for implementation-independent code.

The libpq SSL code is changed to use a custom BIO, which does all the raw
I/O, like we've been doing in the backend for a long time. That makes it
possible to use MSG_NOSIGNAL to block SIGPIPE when using SSL, which avoids
a couple of syscall for each send(). Probably doesn't make much performance
difference in practice - the SSL encryption is expensive enough to mask the
effect - but it was a natural result of this refactoring.

Based on a patch by Martijn van Oosterhout from 2006. Briefly reviewed by
Alvaro Herrera, Andreas Karlsson, Jeff Janes.

26 files changed:
configure
configure.in
src/backend/libpq/Makefile
src/backend/libpq/auth.c
src/backend/libpq/be-secure-openssl.c [new file with mode: 0644]
src/backend/libpq/be-secure.c
src/backend/libpq/hba.c
src/backend/postmaster/fork_process.c
src/backend/utils/init/postinit.c
src/backend/utils/misc/guc.c
src/bin/psql/command.c
src/include/libpq/libpq-be.h
src/include/libpq/libpq.h
src/include/pg_config.h.in
src/include/pg_config.h.win32
src/include/pg_config_manual.h
src/interfaces/libpq/Makefile
src/interfaces/libpq/fe-connect.c
src/interfaces/libpq/fe-misc.c
src/interfaces/libpq/fe-secure-openssl.c [new file with mode: 0644]
src/interfaces/libpq/fe-secure.c
src/interfaces/libpq/libpq-int.h
src/interfaces/libpq/win32.mak
src/tools/msvc/Mkvcbuild.pm
src/tools/msvc/Solution.pm
src/tools/msvc/config_default.pl

index 0b0a6566f36845babb7d4170ab90297098a7dcd2..0f435b5c946b743ba7e90890b5ce70dc6113b50d 100755 (executable)
--- a/configure
+++ b/configure
@@ -5492,7 +5492,7 @@ if test "${with_openssl+set}" = set; then :
   case $withval in
     yes)
 
-$as_echo "#define USE_SSL 1" >>confdefs.h
+$as_echo "#define USE_OPENSSL 1" >>confdefs.h
 
       ;;
     no)
index fd9eb7152c431112c83daa39d080e6af40b2e435..f8a45070634e38b54090fba0e0057f3fc2ddb45d 100644 (file)
@@ -657,7 +657,7 @@ AC_MSG_RESULT([$with_bonjour])
 #
 AC_MSG_CHECKING([whether to build with OpenSSL support])
 PGAC_ARG_BOOL(with, openssl, no, [build with OpenSSL support],
-              [AC_DEFINE([USE_SSL], 1, [Define to build with (Open)SSL support. (--with-openssl)])])
+              [AC_DEFINE([USE_OPENSSL], 1, [Define to build with OpenSSL support. (--with-openssl)])])
 AC_MSG_RESULT([$with_openssl])
 AC_SUBST(with_openssl)
 
index e9298646462a3a5a78f8dd96f1c50ecaf99a109c..8be0572b5b76789ada91f395ee24a544a27461de 100644 (file)
@@ -17,4 +17,8 @@ include $(top_builddir)/src/Makefile.global
 OBJS = be-fsstubs.o be-secure.o auth.o crypt.o hba.o ip.o md5.o pqcomm.o \
        pqformat.o pqsignal.o
 
+ifeq ($(with_openssl),yes)
+OBJS += be-secure-openssl.o
+endif
+
 include $(top_srcdir)/src/backend/common.mk
index 70b0b93982316b1fd985b1eabe720fab66fe0e70..b1974d121cd1696e712c4507267cbe56cc50bc1c 100644 (file)
@@ -161,7 +161,7 @@ static int  pg_SSPI_recvauth(Port *port);
  * RADIUS Authentication
  *----------------------------------------------------------------
  */
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
 #include <openssl/rand.h>
 #endif
 static int CheckRADIUSAuth(Port *port);
@@ -330,7 +330,7 @@ ClientAuthentication(Port *port)
         * already if it didn't verify ok.
         */
 #ifdef USE_SSL
-       if (!port->peer)
+       if (!port->peer_cert_valid)
        {
            ereport(FATAL,
                    (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
@@ -378,7 +378,7 @@ ClientAuthentication(Port *port)
                       (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
                        errmsg("pg_hba.conf rejects replication connection for host \"%s\", user \"%s\", %s",
                               hostinfo, port->user_name,
-                              port->ssl ? _("SSL on") : _("SSL off"))));
+                              port->ssl_in_use ? _("SSL on") : _("SSL off"))));
 #else
                    ereport(FATAL,
                       (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
@@ -394,7 +394,7 @@ ClientAuthentication(Port *port)
                        errmsg("pg_hba.conf rejects connection for host \"%s\", user \"%s\", database \"%s\", %s",
                               hostinfo, port->user_name,
                               port->database_name,
-                              port->ssl ? _("SSL on") : _("SSL off"))));
+                              port->ssl_in_use ? _("SSL on") : _("SSL off"))));
 #else
                    ereport(FATAL,
                       (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
@@ -452,7 +452,7 @@ ClientAuthentication(Port *port)
                       (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
                        errmsg("no pg_hba.conf entry for replication connection from host \"%s\", user \"%s\", %s",
                               hostinfo, port->user_name,
-                              port->ssl ? _("SSL on") : _("SSL off")),
+                              port->ssl_in_use ? _("SSL on") : _("SSL off")),
                        HOSTNAME_LOOKUP_DETAIL(port)));
 #else
                    ereport(FATAL,
@@ -470,7 +470,7 @@ ClientAuthentication(Port *port)
                        errmsg("no pg_hba.conf entry for host \"%s\", user \"%s\", database \"%s\", %s",
                               hostinfo, port->user_name,
                               port->database_name,
-                              port->ssl ? _("SSL on") : _("SSL off")),
+                              port->ssl_in_use ? _("SSL on") : _("SSL off")),
                        HOSTNAME_LOOKUP_DETAIL(port)));
 #else
                    ereport(FATAL,
@@ -2315,7 +2315,7 @@ CheckRADIUSAuth(Port *port)
    /* Construct RADIUS packet */
    packet->code = RADIUS_ACCESS_REQUEST;
    packet->length = RADIUS_HEADER_LENGTH;
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
    if (RAND_bytes(packet->vector, RADIUS_VECTOR_LENGTH) != 1)
    {
        ereport(LOG,
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
new file mode 100644 (file)
index 0000000..e3a284b
--- /dev/null
@@ -0,0 +1,1045 @@
+/*-------------------------------------------------------------------------
+ *
+ * be-secure-openssl.c
+ *   functions for OpenSSL support in the backend.
+ *
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *   src/backend/libpq/be-secure-openssl.c
+ *
+ *   Since the server static private key ($DataDir/server.key)
+ *   will normally be stored unencrypted so that the database
+ *   backend can restart automatically, it is important that
+ *   we select an algorithm that continues to provide confidentiality
+ *   even if the attacker has the server's private key.  Ephemeral
+ *   DH (EDH) keys provide this, and in fact provide Perfect Forward
+ *   Secrecy (PFS) except for situations where the session can
+ *   be hijacked during a periodic handshake/renegotiation.
+ *   Even that backdoor can be closed if client certificates
+ *   are used (since the imposter will be unable to successfully
+ *   complete renegotiation).
+ *
+ *   N.B., the static private key should still be protected to
+ *   the largest extent possible, to minimize the risk of
+ *   impersonations.
+ *
+ *   Another benefit of EDH is that it allows the backend and
+ *   clients to use DSA keys.  DSA keys can only provide digital
+ *   signatures, not encryption, and are often acceptable in
+ *   jurisdictions where RSA keys are unacceptable.
+ *
+ *   The downside to EDH is that it makes it impossible to
+ *   use ssldump(1) if there's a problem establishing an SSL
+ *   session.  In this case you'll need to temporarily disable
+ *   EDH by commenting out the callback.
+ *
+ *   ...
+ *
+ *   Because the risk of cryptanalysis increases as large
+ *   amounts of data are sent with the same session key, the
+ *   session keys are periodically renegotiated.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include <sys/stat.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#endif
+
+#include <openssl/ssl.h>
+#include <openssl/dh.h>
+#if SSLEAY_VERSION_NUMBER >= 0x0907000L
+#include <openssl/conf.h>
+#endif
+#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_ECDH)
+#include <openssl/ec.h>
+#endif
+
+#include "libpq/libpq.h"
+#include "tcop/tcopprot.h"
+#include "utils/memutils.h"
+
+
+
+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 int verify_cb(int, X509_STORE_CTX *);
+static void info_cb(const SSL *ssl, int type, int args);
+static const char *SSLerrmessage(void);
+
+/* are we in the middle of a renegotiation? */
+static bool in_ssl_renegotiation = false;
+
+static SSL_CTX *SSL_context = NULL;
+
+/* ------------------------------------------------------------ */
+/*                      Hardcoded values                       */
+/* ------------------------------------------------------------ */
+
+/*
+ * Hardcoded DH parameters, used in ephemeral 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 DBA... or an attacker... deletes the
+ * $DataDir/dh*.pem files.
+ *
+ * We could refuse SSL connections unless a good DH parameter
+ * file exists, but some clients may quietly renegotiate an
+ * unsecured connection without fully informing the user.
+ * Very uncool.
+ *
+ * Alternatively, the backend could attempt to load these files
+ * on startup if SSL is enabled - and refuse to start if any
+ * do not exist - but this would tend to piss off DBAs.
+ *
+ * If you want to create your own hardcoded DH parameters
+ * for fun and profit, review "Assigned Number for SKIP
+ * Protocols" (http://www.skip-vpn.org/spec/numbers.html)
+ * for suggestions.
+ */
+
+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";
+
+/*
+ * Write data to a secure connection.
+ */
+ssize_t
+be_tls_write(Port *port, void *ptr, size_t len)
+{
+   ssize_t     n;
+   int         err;
+
+   /*
+    * If SSL renegotiations are enabled and we're getting close to the
+    * limit, start one now; but avoid it if there's one already in
+    * progress.  Request the renegotiation 1kB before the limit has
+    * actually expired.
+    */
+   if (ssl_renegotiation_limit && !in_ssl_renegotiation &&
+       port->count > (ssl_renegotiation_limit - 1) * 1024L)
+   {
+       in_ssl_renegotiation = true;
+
+       /*
+        * The way we determine that a renegotiation has completed is by
+        * observing OpenSSL's internal renegotiation counter.  Make sure
+        * we start out at zero, and assume that the renegotiation is
+        * complete when the counter advances.
+        *
+        * OpenSSL provides SSL_renegotiation_pending(), but this doesn't
+        * seem to work in testing.
+        */
+       SSL_clear_num_renegotiations(port->ssl);
+
+       SSL_set_session_id_context(port->ssl, (void *) &SSL_context,
+                                  sizeof(SSL_context));
+       if (SSL_renegotiate(port->ssl) <= 0)
+           ereport(COMMERROR,
+                   (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                    errmsg("SSL failure during renegotiation start")));
+       else
+       {
+           int         retries;
+
+           /*
+            * A handshake can fail, so be prepared to retry it, but only
+            * a few times.
+            */
+           for (retries = 0;; retries++)
+           {
+               if (SSL_do_handshake(port->ssl) > 0)
+                   break;  /* done */
+               ereport(COMMERROR,
+                       (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                        errmsg("SSL handshake failure on renegotiation, retrying")));
+               if (retries >= 20)
+                   ereport(FATAL,
+                           (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                            errmsg("unable to complete SSL handshake")));
+           }
+       }
+   }
+
+wloop:
+   errno = 0;
+   n = SSL_write(port->ssl, ptr, len);
+   err = SSL_get_error(port->ssl, n);
+   switch (err)
+   {
+       case SSL_ERROR_NONE:
+           port->count += n;
+           break;
+       case SSL_ERROR_WANT_READ:
+       case SSL_ERROR_WANT_WRITE:
+#ifdef WIN32
+           pgwin32_waitforsinglesocket(SSL_get_fd(port->ssl),
+                                       (err == SSL_ERROR_WANT_READ) ?
+                                   FD_READ | FD_CLOSE : FD_WRITE | FD_CLOSE,
+                                       INFINITE);
+#endif
+           goto wloop;
+       case SSL_ERROR_SYSCALL:
+           /* leave it to caller to ereport the value of errno */
+           if (n != -1)
+           {
+               errno = ECONNRESET;
+               n = -1;
+           }
+           break;
+       case SSL_ERROR_SSL:
+           ereport(COMMERROR,
+                   (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                    errmsg("SSL error: %s", SSLerrmessage())));
+           /* fall through */
+       case SSL_ERROR_ZERO_RETURN:
+           errno = ECONNRESET;
+           n = -1;
+           break;
+       default:
+           ereport(COMMERROR,
+                   (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                    errmsg("unrecognized SSL error code: %d",
+                           err)));
+           errno = ECONNRESET;
+           n = -1;
+           break;
+   }
+
+   if (n >= 0)
+   {
+       /* is renegotiation complete? */
+       if (in_ssl_renegotiation &&
+           SSL_num_renegotiations(port->ssl) >= 1)
+       {
+           in_ssl_renegotiation = false;
+           port->count = 0;
+       }
+
+       /*
+        * if renegotiation is still ongoing, and we've gone beyond the
+        * limit, kill the connection now -- continuing to use it can be
+        * considered a security problem.
+        */
+       if (in_ssl_renegotiation &&
+           port->count > ssl_renegotiation_limit * 1024L)
+           ereport(FATAL,
+                   (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                    errmsg("SSL failed to renegotiate connection before limit expired")));
+   }
+
+   return n;
+}
+
+/* ------------------------------------------------------------ */
+/*                     OpenSSL specific code                   */
+/* ------------------------------------------------------------ */
+
+/*
+ * Private substitute BIO: this does the sending and receiving using send() and
+ * recv() instead. This is so that we can enable and disable interrupts
+ * just while calling recv(). We cannot have interrupts occurring while
+ * the bulk of openssl runs, because it uses malloc() and possibly other
+ * non-reentrant libc facilities. We also need to call send() and recv()
+ * directly so it gets passed through the socket/signals layer on Win32.
+ *
+ * These functions are closely modelled on the standard socket BIO in OpenSSL;
+ * see sock_read() and sock_write() in OpenSSL's crypto/bio/bss_sock.c.
+ * XXX OpenSSL 1.0.1e considers many more errcodes than just EINTR as reasons
+ * to retry; do we need to adopt their logic for that?
+ */
+
+static bool my_bio_initialized = false;
+static BIO_METHOD my_bio_methods;
+
+static int
+my_sock_read(BIO *h, char *buf, int size)
+{
+   int         res = 0;
+
+   if (buf != NULL)
+   {
+       res = secure_raw_read(((Port *)h->ptr), buf, size);
+       BIO_clear_retry_flags(h);
+       if (res <= 0)
+       {
+           /* If we were interrupted, tell caller to retry */
+           if (errno == EINTR)
+           {
+               BIO_set_retry_read(h);
+           }
+       }
+   }
+
+   return res;
+}
+
+static int
+my_sock_write(BIO *h, const char *buf, int size)
+{
+   int         res = 0;
+
+   res = secure_raw_write(((Port *) h->ptr), buf, size);
+   BIO_clear_retry_flags(h);
+   if (res <= 0)
+   {
+       if (errno == EINTR)
+       {
+           BIO_set_retry_write(h);
+       }
+   }
+
+   return res;
+}
+
+static BIO_METHOD *
+my_BIO_s_socket(void)
+{
+   if (!my_bio_initialized)
+   {
+       memcpy(&my_bio_methods, BIO_s_socket(), sizeof(BIO_METHOD));
+       my_bio_methods.bread = my_sock_read;
+       my_bio_methods.bwrite = my_sock_write;
+       my_bio_initialized = true;
+   }
+   return &my_bio_methods;
+}
+
+/* This should exactly match openssl's SSL_set_fd except for using my BIO */
+static int
+my_SSL_set_fd(Port *port, int fd)
+{
+   int         ret = 0;
+   BIO        *bio = NULL;
+
+   bio = BIO_new(my_BIO_s_socket());
+
+   if (bio == NULL)
+   {
+       SSLerr(SSL_F_SSL_SET_FD, ERR_R_BUF_LIB);
+       goto err;
+   }
+   /* Use 'ptr' to store pointer to PGconn */
+   bio->ptr = port;
+
+   BIO_set_fd(bio, fd, BIO_NOCLOSE);
+   SSL_set_bio(port->ssl, bio, bio);
+   ret = 1;
+err:
+   return ret;
+}
+
+/*
+ * 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)
+{
+   FILE       *fp;
+   char        fnbuf[MAXPGPATH];
+   DH         *dh = NULL;
+   int         codes;
+
+   /* attempt to open file.  It's not an error if it doesn't exist. */
+   snprintf(fnbuf, sizeof(fnbuf), "dh%d.pem", keylength);
+   if ((fp = fopen(fnbuf, "r")) == NULL)
+       return NULL;
+
+/* flock(fileno(fp), LOCK_SH); */
+   dh = PEM_read_DHparams(fp, NULL, NULL, NULL);
+/* flock(fileno(fp), LOCK_UN); */
+   fclose(fp);
+
+   /* is the prime the correct size? */
+   if (dh != NULL && 8 * DH_size(dh) < keylength)
+   {
+       elog(LOG, "DH errors (%s): %d bits expected, %d bits found",
+            fnbuf, keylength, 8 * DH_size(dh));
+       dh = NULL;
+   }
+
+   /* make sure the DH parameters are usable */
+   if (dh != NULL)
+   {
+       if (DH_check(dh, &codes) == 0)
+       {
+           elog(LOG, "DH_check error (%s): %s", fnbuf, SSLerrmessage());
+           return NULL;
+       }
+       if (codes & DH_CHECK_P_NOT_PRIME)
+       {
+           elog(LOG, "DH error (%s): p is not prime", fnbuf);
+           return NULL;
+       }
+       if ((codes & DH_NOT_SUITABLE_GENERATOR) &&
+           (codes & DH_CHECK_P_NOT_SAFE_PRIME))
+       {
+           elog(LOG,
+                "DH error (%s): neither suitable generator or safe prime",
+                fnbuf);
+           return NULL;
+       }
+   }
+
+   return dh;
+}
+
+/*
+ * 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;
+
+   bio = BIO_new_mem_buf((char *) buffer, len);
+   if (bio == NULL)
+       return NULL;
+   dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
+   if (dh == NULL)
+       ereport(DEBUG2,
+               (errmsg_internal("DH load buffer: %s",
+                                SSLerrmessage())));
+   BIO_free(bio);
+
+   return dh;
+}
+
+/*
+ * Generate an ephemeral 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)
+{
+   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)
+   {
+       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;
+   }
+
+   /* this may take a long time, but it may be necessary... */
+   if (r == NULL || 8 * DH_size(r) < keylength)
+   {
+       ereport(DEBUG2,
+               (errmsg_internal("DH: generating parameters (%d bits)",
+                                keylength)));
+       r = DH_generate_parameters(keylength, DH_GENERATOR_2, NULL, NULL);
+   }
+
+   return r;
+}
+
+/*
+ * Certificate verification callback
+ *
+ * This callback allows us to log intermediate problems during
+ * verification, but for now we'll see if the final error message
+ * contains enough information.
+ *
+ * This callback also allows us to override the default acceptance
+ * criteria (e.g., accepting self-signed or expired certs), but
+ * for now we accept the default checks.
+ */
+static int
+verify_cb(int ok, X509_STORE_CTX *ctx)
+{
+   return ok;
+}
+
+/*
+ * This callback is used to copy SSL information messages
+ * into the PostgreSQL log.
+ */
+static void
+info_cb(const SSL *ssl, int type, int args)
+{
+   switch (type)
+   {
+       case SSL_CB_HANDSHAKE_START:
+           ereport(DEBUG4,
+                   (errmsg_internal("SSL: handshake start")));
+           break;
+       case SSL_CB_HANDSHAKE_DONE:
+           ereport(DEBUG4,
+                   (errmsg_internal("SSL: handshake done")));
+           break;
+       case SSL_CB_ACCEPT_LOOP:
+           ereport(DEBUG4,
+                   (errmsg_internal("SSL: accept loop")));
+           break;
+       case SSL_CB_ACCEPT_EXIT:
+           ereport(DEBUG4,
+                   (errmsg_internal("SSL: accept exit (%d)", args)));
+           break;
+       case SSL_CB_CONNECT_LOOP:
+           ereport(DEBUG4,
+                   (errmsg_internal("SSL: connect loop")));
+           break;
+       case SSL_CB_CONNECT_EXIT:
+           ereport(DEBUG4,
+                   (errmsg_internal("SSL: connect exit (%d)", args)));
+           break;
+       case SSL_CB_READ_ALERT:
+           ereport(DEBUG4,
+                   (errmsg_internal("SSL: read alert (0x%04x)", args)));
+           break;
+       case SSL_CB_WRITE_ALERT:
+           ereport(DEBUG4,
+                   (errmsg_internal("SSL: write alert (0x%04x)", args)));
+           break;
+   }
+}
+
+#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_ECDH)
+static void
+initialize_ecdh(void)
+{
+   EC_KEY     *ecdh;
+   int         nid;
+
+   nid = OBJ_sn2nid(SSLECDHCurve);
+   if (!nid)
+       ereport(FATAL,
+               (errmsg("ECDH: unrecognized curve name: %s", SSLECDHCurve)));
+
+   ecdh = EC_KEY_new_by_curve_name(nid);
+   if (!ecdh)
+       ereport(FATAL,
+               (errmsg("ECDH: could not create key")));
+
+   SSL_CTX_set_options(SSL_context, SSL_OP_SINGLE_ECDH_USE);
+   SSL_CTX_set_tmp_ecdh(SSL_context, ecdh);
+   EC_KEY_free(ecdh);
+}
+#else
+#define initialize_ecdh()
+#endif
+
+/*
+ * Initialize global SSL context.
+ */
+void
+be_tls_init(void)
+{
+   struct stat buf;
+
+   STACK_OF(X509_NAME) *root_cert_list = NULL;
+
+   if (!SSL_context)
+   {
+#if SSLEAY_VERSION_NUMBER >= 0x0907000L
+       OPENSSL_config(NULL);
+#endif
+       SSL_library_init();
+       SSL_load_error_strings();
+
+       /*
+        * We use SSLv23_method() because it can negotiate use of the highest
+        * mutually supported protocol version, while alternatives like
+        * TLSv1_2_method() permit only one specific version.  Note that we
+        * don't actually allow SSL v2 or v3, only TLS protocols (see below).
+        */
+       SSL_context = SSL_CTX_new(SSLv23_method());
+       if (!SSL_context)
+           ereport(FATAL,
+                   (errmsg("could not create SSL context: %s",
+                           SSLerrmessage())));
+
+       /*
+        * Disable OpenSSL's moving-write-buffer sanity check, because it
+        * causes unnecessary failures in nonblocking send cases.
+        */
+       SSL_CTX_set_mode(SSL_context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+
+       /*
+        * Load and verify server's certificate and private key
+        */
+       if (SSL_CTX_use_certificate_chain_file(SSL_context,
+                                              ssl_cert_file) != 1)
+           ereport(FATAL,
+                   (errcode(ERRCODE_CONFIG_FILE_ERROR),
+                 errmsg("could not load server certificate file \"%s\": %s",
+                        ssl_cert_file, SSLerrmessage())));
+
+       if (stat(ssl_key_file, &buf) != 0)
+           ereport(FATAL,
+                   (errcode_for_file_access(),
+                    errmsg("could not access private key file \"%s\": %m",
+                           ssl_key_file)));
+
+       /*
+        * Require no public access to key file.
+        *
+        * XXX temporarily suppress check when on Windows, because there may
+        * not be proper support for Unix-y file permissions.  Need to think
+        * of a reasonable check to apply on Windows.  (See also the data
+        * directory permission check in postmaster.c)
+        */
+#if !defined(WIN32) && !defined(__CYGWIN__)
+       if (!S_ISREG(buf.st_mode) || buf.st_mode & (S_IRWXG | S_IRWXO))
+           ereport(FATAL,
+                   (errcode(ERRCODE_CONFIG_FILE_ERROR),
+                 errmsg("private key file \"%s\" has group or world access",
+                        ssl_key_file),
+                  errdetail("Permissions should be u=rw (0600) or less.")));
+#endif
+
+       if (SSL_CTX_use_PrivateKey_file(SSL_context,
+                                       ssl_key_file,
+                                       SSL_FILETYPE_PEM) != 1)
+           ereport(FATAL,
+                   (errmsg("could not load private key file \"%s\": %s",
+                           ssl_key_file, SSLerrmessage())));
+
+       if (SSL_CTX_check_private_key(SSL_context) != 1)
+           ereport(FATAL,
+                   (errmsg("check of private key failed: %s",
+                           SSLerrmessage())));
+   }
+
+   /* set up ephemeral DH keys, and disallow SSL v2/v3 while at it */
+   SSL_CTX_set_tmp_dh_callback(SSL_context, tmp_dh_cb);
+   SSL_CTX_set_options(SSL_context,
+                       SSL_OP_SINGLE_DH_USE |
+                       SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
+
+   /* set up ephemeral ECDH keys */
+   initialize_ecdh();
+
+   /* set up the allowed cipher list */
+   if (SSL_CTX_set_cipher_list(SSL_context, SSLCipherSuites) != 1)
+       elog(FATAL, "could not set the cipher list (no valid ciphers available)");
+
+   /* Let server choose order */
+   if (SSLPreferServerCiphers)
+       SSL_CTX_set_options(SSL_context, SSL_OP_CIPHER_SERVER_PREFERENCE);
+
+   /*
+    * Load CA store, so we can verify client certificates if needed.
+    */
+   if (ssl_ca_file[0])
+   {
+       if (SSL_CTX_load_verify_locations(SSL_context, ssl_ca_file, NULL) != 1 ||
+           (root_cert_list = SSL_load_client_CA_file(ssl_ca_file)) == NULL)
+           ereport(FATAL,
+                   (errmsg("could not load root certificate file \"%s\": %s",
+                           ssl_ca_file, SSLerrmessage())));
+   }
+
+   /*----------
+    * Load the Certificate Revocation List (CRL).
+    * http://searchsecurity.techtarget.com/sDefinition/0,,sid14_gci803160,00.html
+    *----------
+    */
+   if (ssl_crl_file[0])
+   {
+       X509_STORE *cvstore = SSL_CTX_get_cert_store(SSL_context);
+
+       if (cvstore)
+       {
+           /* Set the flags to check against the complete CRL chain */
+           if (X509_STORE_load_locations(cvstore, ssl_crl_file, NULL) == 1)
+           {
+               /* 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);
+#else
+               ereport(LOG,
+               (errmsg("SSL certificate revocation list file \"%s\" ignored",
+                       ssl_crl_file),
+                errdetail("SSL library does not support certificate revocation lists.")));
+#endif
+           }
+           else
+               ereport(FATAL,
+                       (errmsg("could not load SSL certificate revocation list file \"%s\": %s",
+                               ssl_crl_file, SSLerrmessage())));
+       }
+   }
+
+   if (ssl_ca_file[0])
+   {
+       /*
+        * Always ask for SSL client cert, but don't fail if it's not
+        * presented.  We might fail such connections later, depending on what
+        * we find in pg_hba.conf.
+        */
+       SSL_CTX_set_verify(SSL_context,
+                          (SSL_VERIFY_PEER |
+                           SSL_VERIFY_CLIENT_ONCE),
+                          verify_cb);
+
+       /* Set flag to remember CA store is successfully loaded */
+       ssl_loaded_verify_locations = true;
+
+       /*
+        * Tell OpenSSL to send the list of root certs we trust to clients in
+        * CertificateRequests.  This lets a client with a keystore select the
+        * appropriate client certificate to send to us.
+        */
+       SSL_CTX_set_client_CA_list(SSL_context, root_cert_list);
+   }
+}
+
+/*
+ * Attempt to negotiate SSL connection.
+ */
+int
+be_tls_open_server(Port *port)
+{
+   int         r;
+   int         err;
+
+   Assert(!port->ssl);
+   Assert(!port->peer);
+
+   if (!(port->ssl = SSL_new(SSL_context)))
+   {
+       ereport(COMMERROR,
+               (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                errmsg("could not initialize SSL connection: %s",
+                       SSLerrmessage())));
+       be_tls_close(port);
+       return -1;
+   }
+   if (!my_SSL_set_fd(port, port->sock))
+   {
+       ereport(COMMERROR,
+               (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                errmsg("could not set SSL socket: %s",
+                       SSLerrmessage())));
+       be_tls_close(port);
+       return -1;
+   }
+   port->ssl_in_use = true;
+
+aloop:
+   r = SSL_accept(port->ssl);
+   if (r <= 0)
+   {
+       err = SSL_get_error(port->ssl, r);
+       switch (err)
+       {
+           case SSL_ERROR_WANT_READ:
+           case SSL_ERROR_WANT_WRITE:
+#ifdef WIN32
+               pgwin32_waitforsinglesocket(SSL_get_fd(port->ssl),
+                                           (err == SSL_ERROR_WANT_READ) ?
+                       FD_READ | FD_CLOSE | FD_ACCEPT : FD_WRITE | FD_CLOSE,
+                                           INFINITE);
+#endif
+               goto aloop;
+           case SSL_ERROR_SYSCALL:
+               if (r < 0)
+                   ereport(COMMERROR,
+                           (errcode_for_socket_access(),
+                            errmsg("could not accept SSL connection: %m")));
+               else
+                   ereport(COMMERROR,
+                           (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                   errmsg("could not accept SSL connection: EOF detected")));
+               break;
+           case SSL_ERROR_SSL:
+               ereport(COMMERROR,
+                       (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                        errmsg("could not accept SSL connection: %s",
+                               SSLerrmessage())));
+               break;
+           case SSL_ERROR_ZERO_RETURN:
+               ereport(COMMERROR,
+                       (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                  errmsg("could not accept SSL connection: EOF detected")));
+               break;
+           default:
+               ereport(COMMERROR,
+                       (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                        errmsg("unrecognized SSL error code: %d",
+                               err)));
+               break;
+       }
+       be_tls_close(port);
+       return -1;
+   }
+
+   port->count = 0;
+
+   /* Get client certificate, if available. */
+   port->peer = SSL_get_peer_certificate(port->ssl);
+
+   /* and extract the Common Name from it. */
+   port->peer_cn = NULL;
+   port->peer_cert_valid = false;
+   if (port->peer != NULL)
+   {
+       int         len;
+
+       len = X509_NAME_get_text_by_NID(X509_get_subject_name(port->peer),
+                                       NID_commonName, NULL, 0);
+       if (len != -1)
+       {
+           char       *peer_cn;
+
+           peer_cn = MemoryContextAlloc(TopMemoryContext, len + 1);
+           r = X509_NAME_get_text_by_NID(X509_get_subject_name(port->peer),
+                                         NID_commonName, peer_cn, len + 1);
+           peer_cn[len] = '\0';
+           if (r != len)
+           {
+               /* shouldn't happen */
+               pfree(peer_cn);
+               be_tls_close(port);
+               return -1;
+           }
+
+           /*
+            * Reject embedded NULLs in certificate common name to prevent
+            * attacks like CVE-2009-4034.
+            */
+           if (len != strlen(peer_cn))
+           {
+               ereport(COMMERROR,
+                       (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                        errmsg("SSL certificate's common name contains embedded null")));
+               pfree(peer_cn);
+               be_tls_close(port);
+               return -1;
+           }
+
+           port->peer_cn = peer_cn;
+       }
+       port->peer_cert_valid = true;
+   }
+
+   ereport(DEBUG2,
+           (errmsg("SSL connection from \"%s\"",
+                   port->peer_cn ? port->peer_cn : "(anonymous)")));
+
+   /* set up debugging/info callback */
+   SSL_CTX_set_info_callback(SSL_context, info_cb);
+
+   return 0;
+}
+
+/*
+ * Close SSL connection.
+ */
+void
+be_tls_close(Port *port)
+{
+   if (port->ssl)
+   {
+       SSL_shutdown(port->ssl);
+       SSL_free(port->ssl);
+       port->ssl = NULL;
+       port->ssl_in_use = false;
+   }
+
+   if (port->peer)
+   {
+       X509_free(port->peer);
+       port->peer = NULL;
+   }
+
+   if (port->peer_cn)
+   {
+       pfree(port->peer_cn);
+       port->peer_cn = NULL;
+   }
+}
+
+ssize_t
+be_tls_read(Port *port, void *ptr, size_t len)
+{
+   ssize_t     n;
+   int         err;
+
+rloop:
+   errno = 0;
+   n = SSL_read(port->ssl, ptr, len);
+   err = SSL_get_error(port->ssl, n);
+   switch (err)
+   {
+       case SSL_ERROR_NONE:
+           port->count += n;
+           break;
+       case SSL_ERROR_WANT_READ:
+       case SSL_ERROR_WANT_WRITE:
+           if (port->noblock)
+           {
+               errno = EWOULDBLOCK;
+               n = -1;
+               break;
+           }
+#ifdef WIN32
+           pgwin32_waitforsinglesocket(SSL_get_fd(port->ssl),
+                                       (err == SSL_ERROR_WANT_READ) ?
+                                   FD_READ | FD_CLOSE : FD_WRITE | FD_CLOSE,
+                                       INFINITE);
+#endif
+           goto rloop;
+       case SSL_ERROR_SYSCALL:
+           /* leave it to caller to ereport the value of errno */
+           if (n != -1)
+           {
+               errno = ECONNRESET;
+               n = -1;
+           }
+           break;
+       case SSL_ERROR_SSL:
+           ereport(COMMERROR,
+                   (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                    errmsg("SSL error: %s", SSLerrmessage())));
+           /* fall through */
+       case SSL_ERROR_ZERO_RETURN:
+           errno = ECONNRESET;
+           n = -1;
+           break;
+       default:
+           ereport(COMMERROR,
+                   (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                    errmsg("unrecognized SSL error code: %d",
+                           err)));
+           errno = ECONNRESET;
+           n = -1;
+           break;
+   }
+
+   return n;
+}
+
+/*
+ * Obtain reason string for last SSL error
+ *
+ * Some caution is needed here since ERR_reason_error_string will
+ * return NULL if it doesn't recognize the error code.  We don't
+ * want to return NULL ever.
+ */
+static const char *
+SSLerrmessage(void)
+{
+   unsigned long errcode;
+   const char *errreason;
+   static char errbuf[32];
+
+   errcode = ERR_get_error();
+   if (errcode == 0)
+       return _("no SSL error reported");
+   errreason = ERR_reason_error_string(errcode);
+   if (errreason != NULL)
+       return errreason;
+   snprintf(errbuf, sizeof(errbuf), _("SSL error code %lu"), errcode);
+   return errbuf;
+}
+
index 59204cfe80150e0aded5be69952da4c5688334ed..41ec1ad8ad9aeffa69e6dba4b9c8b2e4337afc6a 100644 (file)
  * IDENTIFICATION
  *   src/backend/libpq/be-secure.c
  *
- *   Since the server static private key ($DataDir/server.key)
- *   will normally be stored unencrypted so that the database
- *   backend can restart automatically, it is important that
- *   we select an algorithm that continues to provide confidentiality
- *   even if the attacker has the server's private key.  Ephemeral
- *   DH (EDH) keys provide this, and in fact provide Perfect Forward
- *   Secrecy (PFS) except for situations where the session can
- *   be hijacked during a periodic handshake/renegotiation.
- *   Even that backdoor can be closed if client certificates
- *   are used (since the imposter will be unable to successfully
- *   complete renegotiation).
- *
- *   N.B., the static private key should still be protected to
- *   the largest extent possible, to minimize the risk of
- *   impersonations.
- *
- *   Another benefit of EDH is that it allows the backend and
- *   clients to use DSA keys.  DSA keys can only provide digital
- *   signatures, not encryption, and are often acceptable in
- *   jurisdictions where RSA keys are unacceptable.
- *
- *   The downside to EDH is that it makes it impossible to
- *   use ssldump(1) if there's a problem establishing an SSL
- *   session.  In this case you'll need to temporarily disable
- *   EDH by commenting out the callback.
- *
- *   ...
- *
- *   Because the risk of cryptanalysis increases as large
- *   amounts of data are sent with the same session key, the
- *   session keys are periodically renegotiated.
- *
  *-------------------------------------------------------------------------
  */
 
 #include <arpa/inet.h>
 #endif
 
-#ifdef USE_SSL
-#include <openssl/ssl.h>
-#include <openssl/dh.h>
-#if SSLEAY_VERSION_NUMBER >= 0x0907000L
-#include <openssl/conf.h>
-#endif
-#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_ECDH)
-#include <openssl/ec.h>
-#endif
-#endif   /* USE_SSL */
-
 #include "libpq/libpq.h"
 #include "tcop/tcopprot.h"
 #include "utils/memutils.h"
 
 
-#ifdef USE_SSL
-
-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 int verify_cb(int, X509_STORE_CTX *);
-static void info_cb(const SSL *ssl, int type, int args);
-static void initialize_SSL(void);
-static int open_server_SSL(Port *);
-static void close_SSL(Port *);
-static const char *SSLerrmessage(void);
-#endif
-
 char      *ssl_cert_file;
 char      *ssl_key_file;
 char      *ssl_ca_file;
@@ -105,11 +49,7 @@ char       *ssl_crl_file;
 int            ssl_renegotiation_limit;
 
 #ifdef USE_SSL
-/* are we in the middle of a renegotiation? */
-static bool in_ssl_renegotiation = false;
-
-static SSL_CTX *SSL_context = NULL;
-static bool ssl_loaded_verify_locations = false;
+bool ssl_loaded_verify_locations = false;
 #endif
 
 /* GUC variable controlling SSL cipher list */
@@ -121,73 +61,6 @@ char       *SSLECDHCurve;
 /* GUC variable: if false, prefer client ciphers */
 bool       SSLPreferServerCiphers;
 
-/* ------------------------------------------------------------ */
-/*                      Hardcoded values                       */
-/* ------------------------------------------------------------ */
-
-/*
- * Hardcoded DH parameters, used in ephemeral 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 DBA... or an attacker... deletes the
- * $DataDir/dh*.pem files.
- *
- * We could refuse SSL connections unless a good DH parameter
- * file exists, but some clients may quietly renegotiate an
- * unsecured connection without fully informing the user.
- * Very uncool.
- *
- * Alternatively, the backend could attempt to load these files
- * on startup if SSL is enabled - and refuse to start if any
- * do not exist - but this would tend to piss off DBAs.
- *
- * If you want to create your own hardcoded DH parameters
- * for fun and profit, review "Assigned Number for SKIP
- * Protocols" (http://www.skip-vpn.org/spec/numbers.html)
- * for suggestions.
- */
-#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
-
 /* ------------------------------------------------------------ */
 /*          Procedures common to all secure sessions           */
 /* ------------------------------------------------------------ */
@@ -199,7 +72,7 @@ int
 secure_initialize(void)
 {
 #ifdef USE_SSL
-   initialize_SSL();
+   be_tls_init();
 #endif
 
    return 0;
@@ -227,7 +100,7 @@ secure_open_server(Port *port)
    int         r = 0;
 
 #ifdef USE_SSL
-   r = open_server_SSL(port);
+   r = be_tls_open_server(port);
 #endif
 
    return r;
@@ -240,8 +113,8 @@ void
 secure_close(Port *port)
 {
 #ifdef USE_SSL
-   if (port->ssl)
-       close_SSL(port);
+   if (port->ssl_in_use)
+       be_tls_close(port);
 #endif
 }
 
@@ -254,908 +127,56 @@ secure_read(Port *port, void *ptr, size_t len)
    ssize_t     n;
 
 #ifdef USE_SSL
-   if (port->ssl)
+   if (port->ssl_in_use)
    {
-       int         err;
-
-rloop:
-       errno = 0;
-       n = SSL_read(port->ssl, ptr, len);
-       err = SSL_get_error(port->ssl, n);
-       switch (err)
-       {
-           case SSL_ERROR_NONE:
-               port->count += n;
-               break;
-           case SSL_ERROR_WANT_READ:
-           case SSL_ERROR_WANT_WRITE:
-               if (port->noblock)
-               {
-                   errno = EWOULDBLOCK;
-                   n = -1;
-                   break;
-               }
-#ifdef WIN32
-               pgwin32_waitforsinglesocket(SSL_get_fd(port->ssl),
-                                           (err == SSL_ERROR_WANT_READ) ?
-                                   FD_READ | FD_CLOSE : FD_WRITE | FD_CLOSE,
-                                           INFINITE);
-#endif
-               goto rloop;
-           case SSL_ERROR_SYSCALL:
-               /* leave it to caller to ereport the value of errno */
-               if (n != -1)
-               {
-                   errno = ECONNRESET;
-                   n = -1;
-               }
-               break;
-           case SSL_ERROR_SSL:
-               ereport(COMMERROR,
-                       (errcode(ERRCODE_PROTOCOL_VIOLATION),
-                        errmsg("SSL error: %s", SSLerrmessage())));
-               /* fall through */
-           case SSL_ERROR_ZERO_RETURN:
-               errno = ECONNRESET;
-               n = -1;
-               break;
-           default:
-               ereport(COMMERROR,
-                       (errcode(ERRCODE_PROTOCOL_VIOLATION),
-                        errmsg("unrecognized SSL error code: %d",
-                               err)));
-               errno = ECONNRESET;
-               n = -1;
-               break;
-       }
+       n = be_tls_read(port, ptr, len);
    }
    else
 #endif
    {
-       prepare_for_client_read();
-
-       n = recv(port->sock, ptr, len, 0);
-
-       client_read_ended();
+       n = secure_raw_read(port, ptr, len);
    }
 
    return n;
 }
 
-/*
- * Write data to a secure connection.
- */
 ssize_t
-secure_write(Port *port, void *ptr, size_t len)
+secure_raw_read(Port *port, void *ptr, size_t len)
 {
    ssize_t     n;
 
-#ifdef USE_SSL
-   if (port->ssl)
-   {
-       int         err;
-
-       /*
-        * If SSL renegotiations are enabled and we're getting close to the
-        * limit, start one now; but avoid it if there's one already in
-        * progress.  Request the renegotiation 1kB before the limit has
-        * actually expired.
-        */
-       if (ssl_renegotiation_limit && !in_ssl_renegotiation &&
-           port->count > (ssl_renegotiation_limit - 1) * 1024L)
-       {
-           in_ssl_renegotiation = true;
-
-           /*
-            * The way we determine that a renegotiation has completed is by
-            * observing OpenSSL's internal renegotiation counter.  Make sure
-            * we start out at zero, and assume that the renegotiation is
-            * complete when the counter advances.
-            *
-            * OpenSSL provides SSL_renegotiation_pending(), but this doesn't
-            * seem to work in testing.
-            */
-           SSL_clear_num_renegotiations(port->ssl);
-
-           SSL_set_session_id_context(port->ssl, (void *) &SSL_context,
-                                      sizeof(SSL_context));
-           if (SSL_renegotiate(port->ssl) <= 0)
-               ereport(COMMERROR,
-                       (errcode(ERRCODE_PROTOCOL_VIOLATION),
-                        errmsg("SSL failure during renegotiation start")));
-           else
-           {
-               int         retries;
-
-               /*
-                * A handshake can fail, so be prepared to retry it, but only
-                * a few times.
-                */
-               for (retries = 0;; retries++)
-               {
-                   if (SSL_do_handshake(port->ssl) > 0)
-                       break;  /* done */
-                   ereport(COMMERROR,
-                           (errcode(ERRCODE_PROTOCOL_VIOLATION),
-                            errmsg("SSL handshake failure on renegotiation, retrying")));
-                   if (retries >= 20)
-                       ereport(FATAL,
-                               (errcode(ERRCODE_PROTOCOL_VIOLATION),
-                                errmsg("unable to complete SSL handshake")));
-               }
-           }
-       }
-
-wloop:
-       errno = 0;
-       n = SSL_write(port->ssl, ptr, len);
-       err = SSL_get_error(port->ssl, n);
-       switch (err)
-       {
-           case SSL_ERROR_NONE:
-               port->count += n;
-               break;
-           case SSL_ERROR_WANT_READ:
-           case SSL_ERROR_WANT_WRITE:
-#ifdef WIN32
-               pgwin32_waitforsinglesocket(SSL_get_fd(port->ssl),
-                                           (err == SSL_ERROR_WANT_READ) ?
-                                   FD_READ | FD_CLOSE : FD_WRITE | FD_CLOSE,
-                                           INFINITE);
-#endif
-               goto wloop;
-           case SSL_ERROR_SYSCALL:
-               /* leave it to caller to ereport the value of errno */
-               if (n != -1)
-               {
-                   errno = ECONNRESET;
-                   n = -1;
-               }
-               break;
-           case SSL_ERROR_SSL:
-               ereport(COMMERROR,
-                       (errcode(ERRCODE_PROTOCOL_VIOLATION),
-                        errmsg("SSL error: %s", SSLerrmessage())));
-               /* fall through */
-           case SSL_ERROR_ZERO_RETURN:
-               errno = ECONNRESET;
-               n = -1;
-               break;
-           default:
-               ereport(COMMERROR,
-                       (errcode(ERRCODE_PROTOCOL_VIOLATION),
-                        errmsg("unrecognized SSL error code: %d",
-                               err)));
-               errno = ECONNRESET;
-               n = -1;
-               break;
-       }
-
-       if (n >= 0)
-       {
-           /* is renegotiation complete? */
-           if (in_ssl_renegotiation &&
-               SSL_num_renegotiations(port->ssl) >= 1)
-           {
-               in_ssl_renegotiation = false;
-               port->count = 0;
-           }
-
-           /*
-            * if renegotiation is still ongoing, and we've gone beyond the
-            * limit, kill the connection now -- continuing to use it can be
-            * considered a security problem.
-            */
-           if (in_ssl_renegotiation &&
-               port->count > ssl_renegotiation_limit * 1024L)
-               ereport(FATAL,
-                       (errcode(ERRCODE_PROTOCOL_VIOLATION),
-                        errmsg("SSL failed to renegotiate connection before limit expired")));
-       }
-   }
-   else
-#endif
-       n = send(port->sock, ptr, len, 0);
-
-   return n;
-}
-
-/* ------------------------------------------------------------ */
-/*                       SSL specific code                     */
-/* ------------------------------------------------------------ */
-#ifdef USE_SSL
-
-/*
- * Private substitute BIO: this does the sending and receiving using send() and
- * recv() instead. This is so that we can enable and disable interrupts
- * just while calling recv(). We cannot have interrupts occurring while
- * the bulk of openssl runs, because it uses malloc() and possibly other
- * non-reentrant libc facilities. We also need to call send() and recv()
- * directly so it gets passed through the socket/signals layer on Win32.
- *
- * These functions are closely modelled on the standard socket BIO in OpenSSL;
- * see sock_read() and sock_write() in OpenSSL's crypto/bio/bss_sock.c.
- * XXX OpenSSL 1.0.1e considers many more errcodes than just EINTR as reasons
- * to retry; do we need to adopt their logic for that?
- */
-
-static bool my_bio_initialized = false;
-static BIO_METHOD my_bio_methods;
-
-static int
-my_sock_read(BIO *h, char *buf, int size)
-{
-   int         res = 0;
-
    prepare_for_client_read();
 
-   if (buf != NULL)
-   {
-       res = recv(h->num, buf, size, 0);
-       BIO_clear_retry_flags(h);
-       if (res <= 0)
-       {
-           /* If we were interrupted, tell caller to retry */
-           if (errno == EINTR)
-           {
-               BIO_set_retry_read(h);
-           }
-       }
-   }
+   n = recv(port->sock, ptr, len, 0);
 
    client_read_ended();
 
-   return res;
-}
-
-static int
-my_sock_write(BIO *h, const char *buf, int size)
-{
-   int         res = 0;
-
-   res = send(h->num, buf, size, 0);
-   BIO_clear_retry_flags(h);
-   if (res <= 0)
-   {
-       if (errno == EINTR)
-       {
-           BIO_set_retry_write(h);
-       }
-   }
-
-   return res;
-}
-
-static BIO_METHOD *
-my_BIO_s_socket(void)
-{
-   if (!my_bio_initialized)
-   {
-       memcpy(&my_bio_methods, BIO_s_socket(), sizeof(BIO_METHOD));
-       my_bio_methods.bread = my_sock_read;
-       my_bio_methods.bwrite = my_sock_write;
-       my_bio_initialized = true;
-   }
-   return &my_bio_methods;
-}
-
-/* This should exactly match openssl's SSL_set_fd except for using my BIO */
-static int
-my_SSL_set_fd(SSL *s, int fd)
-{
-   int         ret = 0;
-   BIO        *bio = NULL;
-
-   bio = BIO_new(my_BIO_s_socket());
-
-   if (bio == NULL)
-   {
-       SSLerr(SSL_F_SSL_SET_FD, ERR_R_BUF_LIB);
-       goto err;
-   }
-   BIO_set_fd(bio, fd, BIO_NOCLOSE);
-   SSL_set_bio(s, bio, bio);
-   ret = 1;
-err:
-   return ret;
-}
-
-/*
- * 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)
-{
-   FILE       *fp;
-   char        fnbuf[MAXPGPATH];
-   DH         *dh = NULL;
-   int         codes;
-
-   /* attempt to open file.  It's not an error if it doesn't exist. */
-   snprintf(fnbuf, sizeof(fnbuf), "dh%d.pem", keylength);
-   if ((fp = fopen(fnbuf, "r")) == NULL)
-       return NULL;
-
-/* flock(fileno(fp), LOCK_SH); */
-   dh = PEM_read_DHparams(fp, NULL, NULL, NULL);
-/* flock(fileno(fp), LOCK_UN); */
-   fclose(fp);
-
-   /* is the prime the correct size? */
-   if (dh != NULL && 8 * DH_size(dh) < keylength)
-   {
-       elog(LOG, "DH errors (%s): %d bits expected, %d bits found",
-            fnbuf, keylength, 8 * DH_size(dh));
-       dh = NULL;
-   }
-
-   /* make sure the DH parameters are usable */
-   if (dh != NULL)
-   {
-       if (DH_check(dh, &codes) == 0)
-       {
-           elog(LOG, "DH_check error (%s): %s", fnbuf, SSLerrmessage());
-           return NULL;
-       }
-       if (codes & DH_CHECK_P_NOT_PRIME)
-       {
-           elog(LOG, "DH error (%s): p is not prime", fnbuf);
-           return NULL;
-       }
-       if ((codes & DH_NOT_SUITABLE_GENERATOR) &&
-           (codes & DH_CHECK_P_NOT_SAFE_PRIME))
-       {
-           elog(LOG,
-                "DH error (%s): neither suitable generator or safe prime",
-                fnbuf);
-           return NULL;
-       }
-   }
-
-   return dh;
-}
-
-/*
- * 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;
-
-   bio = BIO_new_mem_buf((char *) buffer, len);
-   if (bio == NULL)
-       return NULL;
-   dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
-   if (dh == NULL)
-       ereport(DEBUG2,
-               (errmsg_internal("DH load buffer: %s",
-                                SSLerrmessage())));
-   BIO_free(bio);
-
-   return dh;
-}
-
-/*
- * Generate an ephemeral 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)
-{
-   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)
-   {
-       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;
-   }
-
-   /* this may take a long time, but it may be necessary... */
-   if (r == NULL || 8 * DH_size(r) < keylength)
-   {
-       ereport(DEBUG2,
-               (errmsg_internal("DH: generating parameters (%d bits)",
-                                keylength)));
-       r = DH_generate_parameters(keylength, DH_GENERATOR_2, NULL, NULL);
-   }
-
-   return r;
-}
-
-/*
- * Certificate verification callback
- *
- * This callback allows us to log intermediate problems during
- * verification, but for now we'll see if the final error message
- * contains enough information.
- *
- * This callback also allows us to override the default acceptance
- * criteria (e.g., accepting self-signed or expired certs), but
- * for now we accept the default checks.
- */
-static int
-verify_cb(int ok, X509_STORE_CTX *ctx)
-{
-   return ok;
-}
-
-/*
- * This callback is used to copy SSL information messages
- * into the PostgreSQL log.
- */
-static void
-info_cb(const SSL *ssl, int type, int args)
-{
-   switch (type)
-   {
-       case SSL_CB_HANDSHAKE_START:
-           ereport(DEBUG4,
-                   (errmsg_internal("SSL: handshake start")));
-           break;
-       case SSL_CB_HANDSHAKE_DONE:
-           ereport(DEBUG4,
-                   (errmsg_internal("SSL: handshake done")));
-           break;
-       case SSL_CB_ACCEPT_LOOP:
-           ereport(DEBUG4,
-                   (errmsg_internal("SSL: accept loop")));
-           break;
-       case SSL_CB_ACCEPT_EXIT:
-           ereport(DEBUG4,
-                   (errmsg_internal("SSL: accept exit (%d)", args)));
-           break;
-       case SSL_CB_CONNECT_LOOP:
-           ereport(DEBUG4,
-                   (errmsg_internal("SSL: connect loop")));
-           break;
-       case SSL_CB_CONNECT_EXIT:
-           ereport(DEBUG4,
-                   (errmsg_internal("SSL: connect exit (%d)", args)));
-           break;
-       case SSL_CB_READ_ALERT:
-           ereport(DEBUG4,
-                   (errmsg_internal("SSL: read alert (0x%04x)", args)));
-           break;
-       case SSL_CB_WRITE_ALERT:
-           ereport(DEBUG4,
-                   (errmsg_internal("SSL: write alert (0x%04x)", args)));
-           break;
-   }
+   return n;
 }
 
-#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_ECDH)
-static void
-initialize_ecdh(void)
-{
-   EC_KEY     *ecdh;
-   int         nid;
-
-   nid = OBJ_sn2nid(SSLECDHCurve);
-   if (!nid)
-       ereport(FATAL,
-               (errmsg("ECDH: unrecognized curve name: %s", SSLECDHCurve)));
-
-   ecdh = EC_KEY_new_by_curve_name(nid);
-   if (!ecdh)
-       ereport(FATAL,
-               (errmsg("ECDH: could not create key")));
-
-   SSL_CTX_set_options(SSL_context, SSL_OP_SINGLE_ECDH_USE);
-   SSL_CTX_set_tmp_ecdh(SSL_context, ecdh);
-   EC_KEY_free(ecdh);
-}
-#else
-#define initialize_ecdh()
-#endif
 
 /*
- * Initialize global SSL context.
- */
-static void
-initialize_SSL(void)
-{
-   struct stat buf;
-
-   STACK_OF(X509_NAME) *root_cert_list = NULL;
-
-   if (!SSL_context)
-   {
-#if SSLEAY_VERSION_NUMBER >= 0x0907000L
-       OPENSSL_config(NULL);
-#endif
-       SSL_library_init();
-       SSL_load_error_strings();
-
-       /*
-        * We use SSLv23_method() because it can negotiate use of the highest
-        * mutually supported protocol version, while alternatives like
-        * TLSv1_2_method() permit only one specific version.  Note that we
-        * don't actually allow SSL v2 or v3, only TLS protocols (see below).
-        */
-       SSL_context = SSL_CTX_new(SSLv23_method());
-       if (!SSL_context)
-           ereport(FATAL,
-                   (errmsg("could not create SSL context: %s",
-                           SSLerrmessage())));
-
-       /*
-        * Disable OpenSSL's moving-write-buffer sanity check, because it
-        * causes unnecessary failures in nonblocking send cases.
-        */
-       SSL_CTX_set_mode(SSL_context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
-
-       /*
-        * Load and verify server's certificate and private key
-        */
-       if (SSL_CTX_use_certificate_chain_file(SSL_context,
-                                              ssl_cert_file) != 1)
-           ereport(FATAL,
-                   (errcode(ERRCODE_CONFIG_FILE_ERROR),
-                 errmsg("could not load server certificate file \"%s\": %s",
-                        ssl_cert_file, SSLerrmessage())));
-
-       if (stat(ssl_key_file, &buf) != 0)
-           ereport(FATAL,
-                   (errcode_for_file_access(),
-                    errmsg("could not access private key file \"%s\": %m",
-                           ssl_key_file)));
-
-       /*
-        * Require no public access to key file.
-        *
-        * XXX temporarily suppress check when on Windows, because there may
-        * not be proper support for Unix-y file permissions.  Need to think
-        * of a reasonable check to apply on Windows.  (See also the data
-        * directory permission check in postmaster.c)
-        */
-#if !defined(WIN32) && !defined(__CYGWIN__)
-       if (!S_ISREG(buf.st_mode) || buf.st_mode & (S_IRWXG | S_IRWXO))
-           ereport(FATAL,
-                   (errcode(ERRCODE_CONFIG_FILE_ERROR),
-                 errmsg("private key file \"%s\" has group or world access",
-                        ssl_key_file),
-                  errdetail("Permissions should be u=rw (0600) or less.")));
-#endif
-
-       if (SSL_CTX_use_PrivateKey_file(SSL_context,
-                                       ssl_key_file,
-                                       SSL_FILETYPE_PEM) != 1)
-           ereport(FATAL,
-                   (errmsg("could not load private key file \"%s\": %s",
-                           ssl_key_file, SSLerrmessage())));
-
-       if (SSL_CTX_check_private_key(SSL_context) != 1)
-           ereport(FATAL,
-                   (errmsg("check of private key failed: %s",
-                           SSLerrmessage())));
-   }
-
-   /* set up ephemeral DH keys, and disallow SSL v2/v3 while at it */
-   SSL_CTX_set_tmp_dh_callback(SSL_context, tmp_dh_cb);
-   SSL_CTX_set_options(SSL_context,
-                       SSL_OP_SINGLE_DH_USE |
-                       SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
-
-   /* set up ephemeral ECDH keys */
-   initialize_ecdh();
-
-   /* set up the allowed cipher list */
-   if (SSL_CTX_set_cipher_list(SSL_context, SSLCipherSuites) != 1)
-       elog(FATAL, "could not set the cipher list (no valid ciphers available)");
-
-   /* Let server choose order */
-   if (SSLPreferServerCiphers)
-       SSL_CTX_set_options(SSL_context, SSL_OP_CIPHER_SERVER_PREFERENCE);
-
-   /*
-    * Load CA store, so we can verify client certificates if needed.
-    */
-   if (ssl_ca_file[0])
-   {
-       if (SSL_CTX_load_verify_locations(SSL_context, ssl_ca_file, NULL) != 1 ||
-           (root_cert_list = SSL_load_client_CA_file(ssl_ca_file)) == NULL)
-           ereport(FATAL,
-                   (errmsg("could not load root certificate file \"%s\": %s",
-                           ssl_ca_file, SSLerrmessage())));
-   }
-
-   /*----------
-    * Load the Certificate Revocation List (CRL).
-    * http://searchsecurity.techtarget.com/sDefinition/0,,sid14_gci803160,00.html
-    *----------
-    */
-   if (ssl_crl_file[0])
-   {
-       X509_STORE *cvstore = SSL_CTX_get_cert_store(SSL_context);
-
-       if (cvstore)
-       {
-           /* Set the flags to check against the complete CRL chain */
-           if (X509_STORE_load_locations(cvstore, ssl_crl_file, NULL) == 1)
-           {
-               /* 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);
-#else
-               ereport(LOG,
-               (errmsg("SSL certificate revocation list file \"%s\" ignored",
-                       ssl_crl_file),
-                errdetail("SSL library does not support certificate revocation lists.")));
-#endif
-           }
-           else
-               ereport(FATAL,
-                       (errmsg("could not load SSL certificate revocation list file \"%s\": %s",
-                               ssl_crl_file, SSLerrmessage())));
-       }
-   }
-
-   if (ssl_ca_file[0])
-   {
-       /*
-        * Always ask for SSL client cert, but don't fail if it's not
-        * presented.  We might fail such connections later, depending on what
-        * we find in pg_hba.conf.
-        */
-       SSL_CTX_set_verify(SSL_context,
-                          (SSL_VERIFY_PEER |
-                           SSL_VERIFY_CLIENT_ONCE),
-                          verify_cb);
-
-       /* Set flag to remember CA store is successfully loaded */
-       ssl_loaded_verify_locations = true;
-
-       /*
-        * Tell OpenSSL to send the list of root certs we trust to clients in
-        * CertificateRequests.  This lets a client with a keystore select the
-        * appropriate client certificate to send to us.
-        */
-       SSL_CTX_set_client_CA_list(SSL_context, root_cert_list);
-   }
-}
-
-/*
- * Attempt to negotiate SSL connection.
+ * Write data to a secure connection.
  */
-static int
-open_server_SSL(Port *port)
+ssize_t
+secure_write(Port *port, void *ptr, size_t len)
 {
-   int         r;
-   int         err;
-
-   Assert(!port->ssl);
-   Assert(!port->peer);
+   ssize_t     n;
 
-   if (!(port->ssl = SSL_new(SSL_context)))
-   {
-       ereport(COMMERROR,
-               (errcode(ERRCODE_PROTOCOL_VIOLATION),
-                errmsg("could not initialize SSL connection: %s",
-                       SSLerrmessage())));
-       close_SSL(port);
-       return -1;
-   }
-   if (!my_SSL_set_fd(port->ssl, port->sock))
+#ifdef USE_SSL
+   if (port->ssl_in_use)
    {
-       ereport(COMMERROR,
-               (errcode(ERRCODE_PROTOCOL_VIOLATION),
-                errmsg("could not set SSL socket: %s",
-                       SSLerrmessage())));
-       close_SSL(port);
-       return -1;
+       n = be_tls_write(port, ptr, len);
    }
-
-aloop:
-   r = SSL_accept(port->ssl);
-   if (r <= 0)
-   {
-       err = SSL_get_error(port->ssl, r);
-       switch (err)
-       {
-           case SSL_ERROR_WANT_READ:
-           case SSL_ERROR_WANT_WRITE:
-#ifdef WIN32
-               pgwin32_waitforsinglesocket(SSL_get_fd(port->ssl),
-                                           (err == SSL_ERROR_WANT_READ) ?
-                       FD_READ | FD_CLOSE | FD_ACCEPT : FD_WRITE | FD_CLOSE,
-                                           INFINITE);
+   else
 #endif
-               goto aloop;
-           case SSL_ERROR_SYSCALL:
-               if (r < 0)
-                   ereport(COMMERROR,
-                           (errcode_for_socket_access(),
-                            errmsg("could not accept SSL connection: %m")));
-               else
-                   ereport(COMMERROR,
-                           (errcode(ERRCODE_PROTOCOL_VIOLATION),
-                   errmsg("could not accept SSL connection: EOF detected")));
-               break;
-           case SSL_ERROR_SSL:
-               ereport(COMMERROR,
-                       (errcode(ERRCODE_PROTOCOL_VIOLATION),
-                        errmsg("could not accept SSL connection: %s",
-                               SSLerrmessage())));
-               break;
-           case SSL_ERROR_ZERO_RETURN:
-               ereport(COMMERROR,
-                       (errcode(ERRCODE_PROTOCOL_VIOLATION),
-                  errmsg("could not accept SSL connection: EOF detected")));
-               break;
-           default:
-               ereport(COMMERROR,
-                       (errcode(ERRCODE_PROTOCOL_VIOLATION),
-                        errmsg("unrecognized SSL error code: %d",
-                               err)));
-               break;
-       }
-       close_SSL(port);
-       return -1;
-   }
-
-   port->count = 0;
-
-   /* Get client certificate, if available. */
-   port->peer = SSL_get_peer_certificate(port->ssl);
-
-   /* and extract the Common Name from it. */
-   port->peer_cn = NULL;
-   if (port->peer != NULL)
-   {
-       int         len;
+       n = secure_raw_write(port, ptr, len);
 
-       len = X509_NAME_get_text_by_NID(X509_get_subject_name(port->peer),
-                                       NID_commonName, NULL, 0);
-       if (len != -1)
-       {
-           char       *peer_cn;
-
-           peer_cn = MemoryContextAlloc(TopMemoryContext, len + 1);
-           r = X509_NAME_get_text_by_NID(X509_get_subject_name(port->peer),
-                                         NID_commonName, peer_cn, len + 1);
-           peer_cn[len] = '\0';
-           if (r != len)
-           {
-               /* shouldn't happen */
-               pfree(peer_cn);
-               close_SSL(port);
-               return -1;
-           }
-
-           /*
-            * Reject embedded NULLs in certificate common name to prevent
-            * attacks like CVE-2009-4034.
-            */
-           if (len != strlen(peer_cn))
-           {
-               ereport(COMMERROR,
-                       (errcode(ERRCODE_PROTOCOL_VIOLATION),
-                        errmsg("SSL certificate's common name contains embedded null")));
-               pfree(peer_cn);
-               close_SSL(port);
-               return -1;
-           }
-
-           port->peer_cn = peer_cn;
-       }
-   }
-
-   ereport(DEBUG2,
-           (errmsg("SSL connection from \"%s\"",
-                   port->peer_cn ? port->peer_cn : "(anonymous)")));
-
-   /* set up debugging/info callback */
-   SSL_CTX_set_info_callback(SSL_context, info_cb);
-
-   return 0;
-}
-
-/*
- * Close SSL connection.
- */
-static void
-close_SSL(Port *port)
-{
-   if (port->ssl)
-   {
-       SSL_shutdown(port->ssl);
-       SSL_free(port->ssl);
-       port->ssl = NULL;
-   }
-
-   if (port->peer)
-   {
-       X509_free(port->peer);
-       port->peer = NULL;
-   }
-
-   if (port->peer_cn)
-   {
-       pfree(port->peer_cn);
-       port->peer_cn = NULL;
-   }
+   return n;
 }
 
-/*
- * Obtain reason string for last SSL error
- *
- * Some caution is needed here since ERR_reason_error_string will
- * return NULL if it doesn't recognize the error code.  We don't
- * want to return NULL ever.
- */
-static const char *
-SSLerrmessage(void)
+ssize_t
+secure_raw_write(Port *port, const void *ptr, size_t len)
 {
-   unsigned long errcode;
-   const char *errreason;
-   static char errbuf[32];
-
-   errcode = ERR_get_error();
-   if (errcode == 0)
-       return _("no SSL error reported");
-   errreason = ERR_reason_error_string(errcode);
-   if (errreason != NULL)
-       return errreason;
-   snprintf(errbuf, sizeof(errbuf), _("SSL error code %lu"), errcode);
-   return errbuf;
+   return send(port->sock, ptr, len, 0);
 }
-
-#endif   /* USE_SSL */
index fd98c60ddb0589a58c9126cb2fec2ea403165deb..84da823ffab782fbaed1afd47d78a41f976e5f44 100644 (file)
@@ -1685,7 +1685,7 @@ check_hba(hbaPort *port)
 
            /* Check SSL state */
 #ifdef USE_SSL
-           if (port->ssl)
+           if (port->ssl_in_use)
            {
                /* Connection is SSL, match both "host" and "hostssl" */
                if (hba->conntype == ctHostNoSSL)
index 5e5bd35e7e3c78149498ebd11bfb84d9e7483d21..dd3ababc5a2e9e12cdd9b8774af74ddcc6e25479 100644 (file)
@@ -17,7 +17,7 @@
 #include <sys/stat.h>
 #include <sys/time.h>
 #include <unistd.h>
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
 #include <openssl/rand.h>
 #endif
 
@@ -110,7 +110,7 @@ fork_process(void)
        /*
         * Make sure processes do not share OpenSSL randomness state.
         */
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
        RAND_cleanup();
 #endif
    }
index 28243ad58f98517025abe30134b7929077b703d0..a5b98217739e7bc1a7d331aa0169829ac1d93e14 100644 (file)
@@ -231,8 +231,8 @@ PerformAuthentication(Port *port)
    {
        if (am_walsender)
        {
-#ifdef USE_SSL
-           if (port->ssl)
+#ifdef USE_OPENSSL
+           if (port->ssl_in_use)
                ereport(LOG,
                        (errmsg("replication connection authorized: user=%s SSL enabled (protocol=%s, cipher=%s, compression=%s)",
                                port->user_name, SSL_get_version(port->ssl), SSL_get_cipher(port->ssl),
@@ -245,8 +245,8 @@ PerformAuthentication(Port *port)
        }
        else
        {
-#ifdef USE_SSL
-           if (port->ssl)
+#ifdef USE_OPENSSL
+           if (port->ssl_in_use)
                ereport(LOG,
                        (errmsg("connection authorized: user=%s database=%s SSL enabled (protocol=%s, cipher=%s, compression=%s)",
                                port->user_name, port->database_name, SSL_get_version(port->ssl), SSL_get_cipher(port->ssl),
index 6c52db859032af33eccb1eb53a890506f4e7b181..9aa1bc4702a4191f4518e9d25c32f90126364c13 100644 (file)
@@ -125,9 +125,6 @@ extern char *default_tablespace;
 extern char *temp_tablespaces;
 extern bool ignore_checksum_failure;
 extern bool synchronize_seqscans;
-extern char *SSLCipherSuites;
-extern char *SSLECDHCurve;
-extern bool SSLPreferServerCiphers;
 
 #ifdef TRACE_SORT
 extern bool trace_sort;
index 741a72d9254cf0ba507485166aabc3434097e062..e27ff8c6501e4107dbaf133c798e4f15bd06d697 100644 (file)
@@ -30,7 +30,7 @@
 #include <sys/types.h>         /* for umask() */
 #include <sys/stat.h>          /* for stat() */
 #endif
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
 #include <openssl/ssl.h>
 #endif
 
@@ -1791,7 +1791,7 @@ connection_warnings(bool in_startup)
 static void
 printSSLInfo(void)
 {
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
    int         sslbits = -1;
    SSL        *ssl;
 
index e78c565b1eaaf43c41e469cfeb6e37f024d0f5ec..34e52e44b0c83c6e847203e0f89e94e3e69478ce 100644 (file)
@@ -21,7 +21,7 @@
 #ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
 #endif
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
 #include <openssl/ssl.h>
 #include <openssl/err.h>
 #endif
@@ -184,17 +184,33 @@ typedef struct Port
 #endif
 
    /*
-    * SSL structures (keep these last so that USE_SSL doesn't affect
-    * locations of other fields)
+    * SSL structures (keep these last so that the locations of other fields
+    * are the same whether or not you build with SSL)
     */
 #ifdef USE_SSL
+   bool        ssl_in_use;
+   char       *peer_cn;
+   bool        peer_cert_valid;
+#endif
+#ifdef USE_OPENSSL
    SSL        *ssl;
    X509       *peer;
-   char       *peer_cn;
    unsigned long count;
 #endif
 } Port;
 
+#ifdef USE_SSL
+/*
+ * These functions are implemented by the glue code specific to each
+ * SSL implementation (e.g. be-secure-openssl.c)
+ */
+extern void be_tls_init(void);
+extern int be_tls_open_server(Port *port);
+extern void be_tls_close(Port *port);
+extern ssize_t be_tls_read(Port *port, void *ptr, size_t len);
+extern ssize_t be_tls_write(Port *port, void *ptr, size_t len);
+
+#endif
 
 extern ProtocolVersion FrontendProtocol;
 
index e4e354dafa05a6ddf50e81eff26a91ddd1bf577c..5da9d8d4f52b9ade54765c819fd80b8e8dd385ae 100644 (file)
@@ -82,5 +82,14 @@ extern int   secure_open_server(Port *port);
 extern void secure_close(Port *port);
 extern ssize_t secure_read(Port *port, void *ptr, size_t len);
 extern ssize_t secure_write(Port *port, void *ptr, size_t len);
+extern ssize_t secure_raw_read(Port *port, void *ptr, size_t len);
+extern ssize_t secure_raw_write(Port *port, const void *ptr, size_t len);
+
+extern bool ssl_loaded_verify_locations;
+
+/* GUCs */
+extern char *SSLCipherSuites;
+extern char *SSLECDHCurve;
+extern bool SSLPreferServerCiphers;
 
 #endif   /* LIBPQ_H */
index 4383ad5172c92c8065db34ee12cde030e932c2ad..5bdfa470dcf8a1be1e403f4b37c918e86796919b 100644 (file)
 /* Define to select named POSIX semaphores. */
 #undef USE_NAMED_POSIX_SEMAPHORES
 
+/* Define to build with OpenSSL support. (--with-openssl) */
+#undef USE_OPENSSL
+
 /* Define to 1 to build with PAM support. (--with-pam) */
 #undef USE_PAM
 
 /* Use replacement snprintf() functions. */
 #undef USE_REPL_SNPRINTF
 
-/* Define to build with (Open)SSL support. (--with-openssl) */
-#undef USE_SSL
-
 /* Define to select SysV-style semaphores. */
 #undef USE_SYSV_SEMAPHORES
 
index f7c2419252bcef89909aeeb7e4880b6ece5d0849..00be15f230ee2bb109ef2a97cdf80099c1b84e49 100644 (file)
 /* Define to select named POSIX semaphores. */
 /* #undef USE_NAMED_POSIX_SEMAPHORES */
 
+/* Define to build with OpenSSL support. (--with-openssl) */
+/* #undef USE_OPENSSL */
+
 /* Define to 1 to build with PAM support. (--with-pam) */
 /* #undef USE_PAM */
 
 /* Use replacement snprintf() functions. */
 #define USE_REPL_SNPRINTF 1
 
-/* Define to build with (Open)SSL support. (--with-openssl) */
-/* #undef USE_SSL */
-
 /* Define to select SysV-style semaphores. */
 /* #undef USE_SYSV_SEMAPHORES */
 
index 16f7ef9bea6d761d2fe19f425df08065c1830fc1..d78f38e3bd5fdd9bd8d2da43cd2c2c2e86f27105 100644 (file)
 #define USE_PREFETCH
 #endif
 
+/*
+ * USE_SSL code should be compiled only when compiling with an SSL
+ * implementation.  (Currently, only OpenSSL is supported, but we might add
+ * more implementations in the future.)
+ */
+#ifdef USE_OPENSSL
+#define USE_SSL
+#endif
+
 /*
  * This is the default directory in which AF_UNIX socket files are
  * placed.  Caution: changing this risks breaking your existing client
index 718ecd686c48265aced3a6e40b8344d3b92abced..a90cb892dec27a8509b5b9f400897f5f3a7da5d4 100644 (file)
@@ -44,6 +44,10 @@ OBJS += ip.o md5.o
 # utils/mb
 OBJS += encnames.o wchar.o
 
+ifeq ($(with_openssl),yes)
+OBJS += fe-secure-openssl.o
+endif
+
 ifeq ($(PORTNAME), cygwin)
 override shlib = cyg$(NAME)$(DLSUFFIX)
 endif
index 540426cbe96c2f40617dacd97a89737c329fef75..b0b0e1a6431ced054b86731666a55fa1db6653a3 100644 (file)
@@ -1961,7 +1961,7 @@ keep_going:                       /* We will come back to here until there is
                    conn->allow_ssl_try = false;
                }
                if (conn->allow_ssl_try && !conn->wait_ssl_try &&
-                   conn->ssl == NULL)
+                   !conn->ssl_in_use)
                {
                    ProtocolVersion pv;
 
@@ -2040,7 +2040,7 @@ keep_going:                       /* We will come back to here until there is
                 * On first time through, get the postmaster's response to our
                 * SSL negotiation packet.
                 */
-               if (conn->ssl == NULL)
+               if (!conn->ssl_in_use)
                {
                    /*
                     * We use pqReadData here since it has the logic to
@@ -2310,7 +2310,7 @@ keep_going:                       /* We will come back to here until there is
                     * connection already, then retry with an SSL connection
                     */
                    if (conn->sslmode[0] == 'a' /* "allow" */
-                       && conn->ssl == NULL
+                       && !conn->ssl_in_use
                        && conn->allow_ssl_try
                        && conn->wait_ssl_try)
                    {
@@ -2709,6 +2709,7 @@ makeEmptyPGconn(void)
 #ifdef USE_SSL
    conn->allow_ssl_try = true;
    conn->wait_ssl_try = false;
+   conn->ssl_in_use = false;
 #endif
 
    /*
index a75db19ae4366144f34bbf730c08dd8135e93a9f..fc930bd05b8e3e6aff3c297f852d7e2e7370be60 100644 (file)
@@ -751,7 +751,7 @@ retry3:
     */
 
 #ifdef USE_SSL
-   if (conn->ssl)
+   if (conn->ssl_in_use)
        return 0;
 #endif
 
@@ -1051,7 +1051,7 @@ pqSocketCheck(PGconn *conn, int forRead, int forWrite, time_t end_time)
        return -1;
    }
 
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
    /* Check for SSL library buffering read bytes */
    if (forRead && conn->ssl && SSL_pending(conn->ssl) > 0)
    {
diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
new file mode 100644 (file)
index 0000000..f950fc3
--- /dev/null
@@ -0,0 +1,1468 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-secure-openssl.c
+ *   OpenSSL support
+ *
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *   src/interfaces/libpq/fe-secure-openssl.c
+ *
+ * NOTES
+ *
+ *   We don't provide informational callbacks here (like
+ *   info_cb() in be-secure.c), since there's no good mechanism to
+ *   display such information to the user.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres_fe.h"
+
+#include <signal.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#include "libpq-fe.h"
+#include "fe-auth.h"
+#include "libpq-int.h"
+
+#ifdef WIN32
+#include "win32.h"
+#else
+#include <sys/socket.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+#include <arpa/inet.h>
+#endif
+
+#include <sys/stat.h>
+
+#ifdef ENABLE_THREAD_SAFETY
+#ifdef WIN32
+#include "pthread-win32.h"
+#else
+#include <pthread.h>
+#endif
+#endif
+
+#include <openssl/ssl.h>
+#if (SSLEAY_VERSION_NUMBER >= 0x00907000L)
+#include <openssl/conf.h>
+#endif
+#ifdef USE_SSL_ENGINE
+#include <openssl/engine.h>
+#endif
+
+static bool verify_peer_name_matches_certificate(PGconn *);
+static int verify_cb(int ok, X509_STORE_CTX *ctx);
+static void destroy_ssl_system(void);
+static int initialize_SSL(PGconn *conn);
+static PostgresPollingStatusType open_client_SSL(PGconn *);
+static char *SSLerrmessage(void);
+static void SSLerrfree(char *buf);
+
+static int my_sock_read(BIO *h, char *buf, int size);
+static int my_sock_write(BIO *h, const char *buf, int size);
+static BIO_METHOD *my_BIO_s_socket(void);
+static int my_SSL_set_fd(PGconn *conn, int fd);
+
+
+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
+static long 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 */
+
+
+/* ------------------------------------------------------------ */
+/*          Procedures common to all secure sessions           */
+/* ------------------------------------------------------------ */
+
+/*
+ * Exported function to allow application to tell us it's already
+ * initialized OpenSSL and/or libcrypto.
+ */
+void
+pgtls_init_library(bool do_ssl, int do_crypto)
+{
+#ifdef ENABLE_THREAD_SAFETY
+
+   /*
+    * Disallow changing the flags while we have open connections, else we'd
+    * get completely confused.
+    */
+   if (ssl_open_connections != 0)
+       return;
+#endif
+
+   pq_init_ssl_lib = do_ssl;
+   pq_init_crypto_lib = do_crypto;
+}
+
+/*
+ * Begin or continue negotiating a secure session.
+ */
+PostgresPollingStatusType
+pgtls_open_client(PGconn *conn)
+{
+   /* First time through? */
+   if (conn->ssl == NULL)
+   {
+#ifdef ENABLE_THREAD_SAFETY
+       int         rc;
+#endif
+
+#ifdef ENABLE_THREAD_SAFETY
+       if ((rc = pthread_mutex_lock(&ssl_config_mutex)))
+       {
+           printfPQExpBuffer(&conn->errorMessage,
+              libpq_gettext("could not acquire mutex: %s\n"), strerror(rc));
+           return PGRES_POLLING_FAILED;
+       }
+#endif
+       /* Create a connection-specific SSL object */
+       if (!(conn->ssl = SSL_new(SSL_context)) ||
+           !SSL_set_app_data(conn->ssl, conn) ||
+           !my_SSL_set_fd(conn, conn->sock))
+       {
+           char       *err = SSLerrmessage();
+
+           printfPQExpBuffer(&conn->errorMessage,
+                  libpq_gettext("could not establish SSL connection: %s\n"),
+                             err);
+           SSLerrfree(err);
+#ifdef ENABLE_THREAD_SAFETY
+           pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+           pgtls_close(conn);
+
+           return PGRES_POLLING_FAILED;
+       }
+       conn->ssl_in_use = true;
+
+#ifdef ENABLE_THREAD_SAFETY
+       pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+
+       /*
+        * Load client certificate, private key, and trusted CA certs.
+        */
+       if (initialize_SSL(conn) != 0)
+       {
+           /* initialize_SSL already put a message in conn->errorMessage */
+           pgtls_close(conn);
+           return PGRES_POLLING_FAILED;
+       }
+   }
+
+   /* Begin or continue the actual handshake */
+   return open_client_SSL(conn);
+}
+
+/*
+ * Read data from a secure connection.
+ *
+ * On failure, this function is responsible for putting a suitable message
+ * into conn->errorMessage.  The caller must still inspect errno, but only
+ * to determine whether to continue/retry after error.
+ */
+ssize_t
+pgtls_read(PGconn *conn, void *ptr, size_t len)
+{
+   ssize_t     n;
+   int         result_errno = 0;
+   char        sebuf[256];
+   int         err;
+
+rloop:
+   SOCK_ERRNO_SET(0);
+   n = SSL_read(conn->ssl, ptr, len);
+   err = SSL_get_error(conn->ssl, n);
+   switch (err)
+   {
+       case SSL_ERROR_NONE:
+           if (n < 0)
+           {
+               /* Not supposed to happen, so we don't translate the msg */
+               printfPQExpBuffer(&conn->errorMessage,
+                                 "SSL_read failed but did not provide error information\n");
+               /* assume the connection is broken */
+               result_errno = ECONNRESET;
+           }
+           break;
+       case SSL_ERROR_WANT_READ:
+           n = 0;
+           break;
+       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.
+            */
+           goto rloop;
+       case SSL_ERROR_SYSCALL:
+           if (n < 0)
+           {
+               result_errno = SOCK_ERRNO;
+               if (result_errno == EPIPE ||
+                   result_errno == ECONNRESET)
+                   printfPQExpBuffer(&conn->errorMessage,
+                                     libpq_gettext(
+                               "server closed the connection unexpectedly\n"
+                                                   "\tThis probably means the server terminated abnormally\n"
+                            "\tbefore or while processing the request.\n"));
+               else
+                   printfPQExpBuffer(&conn->errorMessage,
+                                   libpq_gettext("SSL SYSCALL error: %s\n"),
+                                     SOCK_STRERROR(result_errno,
+                                                   sebuf, sizeof(sebuf)));
+           }
+           else
+           {
+               printfPQExpBuffer(&conn->errorMessage,
+                        libpq_gettext("SSL SYSCALL error: EOF detected\n"));
+               /* assume the connection is broken */
+               result_errno = ECONNRESET;
+               n = -1;
+           }
+           break;
+       case SSL_ERROR_SSL:
+           {
+               char       *errm = SSLerrmessage();
+
+               printfPQExpBuffer(&conn->errorMessage,
+                                 libpq_gettext("SSL error: %s\n"), errm);
+               SSLerrfree(errm);
+               /* assume the connection is broken */
+               result_errno = ECONNRESET;
+               n = -1;
+               break;
+           }
+       case SSL_ERROR_ZERO_RETURN:
+
+           /*
+            * Per OpenSSL documentation, this error code is only returned
+            * for a clean connection closure, so we should not report it
+            * as a server crash.
+            */
+           printfPQExpBuffer(&conn->errorMessage,
+                             libpq_gettext("SSL connection has been closed unexpectedly\n"));
+           result_errno = ECONNRESET;
+           n = -1;
+           break;
+       default:
+           printfPQExpBuffer(&conn->errorMessage,
+                         libpq_gettext("unrecognized SSL error code: %d\n"),
+                             err);
+           /* assume the connection is broken */
+           result_errno = ECONNRESET;
+           n = -1;
+           break;
+   }
+
+   /* ensure we return the intended errno to caller */
+   SOCK_ERRNO_SET(result_errno);
+
+   return n;
+}
+
+/*
+ * Write data to a secure connection.
+ *
+ * On failure, this function is responsible for putting a suitable message
+ * into conn->errorMessage.  The caller must still inspect errno, but only
+ * to determine whether to continue/retry after error.
+ */
+ssize_t
+pgtls_write(PGconn *conn, const void *ptr, size_t len)
+{
+   ssize_t     n;
+   int         result_errno = 0;
+   char        sebuf[256];
+   int         err;
+
+   SOCK_ERRNO_SET(0);
+   n = SSL_write(conn->ssl, ptr, len);
+   err = SSL_get_error(conn->ssl, n);
+   switch (err)
+   {
+       case SSL_ERROR_NONE:
+           if (n < 0)
+           {
+               /* Not supposed to happen, so we don't translate the msg */
+               printfPQExpBuffer(&conn->errorMessage,
+                                 "SSL_write failed but did not provide error information\n");
+               /* assume the connection is broken */
+               result_errno = ECONNRESET;
+           }
+           break;
+       case SSL_ERROR_WANT_READ:
+
+           /*
+            * 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.
+            */
+           n = 0;
+           break;
+       case SSL_ERROR_WANT_WRITE:
+           n = 0;
+           break;
+       case SSL_ERROR_SYSCALL:
+           if (n < 0)
+           {
+               result_errno = SOCK_ERRNO;
+               if (result_errno == EPIPE || result_errno == ECONNRESET)
+                   printfPQExpBuffer(&conn->errorMessage,
+                                     libpq_gettext(
+                               "server closed the connection unexpectedly\n"
+                  "\tThis probably means the server terminated abnormally\n"
+                            "\tbefore or while processing the request.\n"));
+               else
+                   printfPQExpBuffer(&conn->errorMessage,
+                                   libpq_gettext("SSL SYSCALL error: %s\n"),
+                                     SOCK_STRERROR(result_errno,
+                                                   sebuf, sizeof(sebuf)));
+           }
+           else
+           {
+               printfPQExpBuffer(&conn->errorMessage,
+                        libpq_gettext("SSL SYSCALL error: EOF detected\n"));
+               /* assume the connection is broken */
+               result_errno = ECONNRESET;
+               n = -1;
+           }
+           break;
+       case SSL_ERROR_SSL:
+           {
+               char       *errm = SSLerrmessage();
+
+               printfPQExpBuffer(&conn->errorMessage,
+                                 libpq_gettext("SSL error: %s\n"), errm);
+               SSLerrfree(errm);
+               /* assume the connection is broken */
+               result_errno = ECONNRESET;
+               n = -1;
+               break;
+           }
+       case SSL_ERROR_ZERO_RETURN:
+
+           /*
+            * Per OpenSSL documentation, this error code is only returned
+            * for a clean connection closure, so we should not report it
+            * as a server crash.
+            */
+           printfPQExpBuffer(&conn->errorMessage,
+                             libpq_gettext("SSL connection has been closed unexpectedly\n"));
+           result_errno = ECONNRESET;
+           n = -1;
+           break;
+       default:
+           printfPQExpBuffer(&conn->errorMessage,
+                         libpq_gettext("unrecognized SSL error code: %d\n"),
+                             err);
+           /* assume the connection is broken */
+           result_errno = ECONNRESET;
+           n = -1;
+           break;
+   }
+
+   /* ensure we return the intended errno to caller */
+   SOCK_ERRNO_SET(result_errno);
+
+   return n;
+}
+
+/* ------------------------------------------------------------ */
+/*                     OpenSSL specific code                   */
+/* ------------------------------------------------------------ */
+
+/*
+ * Certificate verification callback
+ *
+ * This callback allows us to log intermediate problems during
+ * verification, but there doesn't seem to be a clean way to get
+ * our PGconn * structure.  So we can't log anything!
+ *
+ * This callback also allows us to override the default acceptance
+ * criteria (e.g., accepting self-signed or expired certs), but
+ * for now we accept the default checks.
+ */
+static int
+verify_cb(int ok, X509_STORE_CTX *ctx)
+{
+   return ok;
+}
+
+
+/*
+ * Check if a wildcard certificate matches the server hostname.
+ *
+ * The rule for this is:
+ * 1. We only match the '*' character as wildcard
+ * 2. We match only wildcards at the start of the string
+ * 3. The '*' character does *not* match '.', meaning that we match only
+ *    a single pathname component.
+ * 4. We don't support more than one '*' in a single pattern.
+ *
+ * This is roughly in line with RFC2818, but contrary to what most browsers
+ * appear to be implementing (point 3 being the difference)
+ *
+ * Matching is always case-insensitive, since DNS is case insensitive.
+ */
+static int
+wildcard_certificate_match(const char *pattern, const char *string)
+{
+   int         lenpat = strlen(pattern);
+   int         lenstr = strlen(string);
+
+   /* If we don't start with a wildcard, it's not a match (rule 1 & 2) */
+   if (lenpat < 3 ||
+       pattern[0] != '*' ||
+       pattern[1] != '.')
+       return 0;
+
+   if (lenpat > lenstr)
+       /* If pattern is longer than the string, we can never match */
+       return 0;
+
+   if (pg_strcasecmp(pattern + 1, string + lenstr - lenpat + 1) != 0)
+
+       /*
+        * If string does not end in pattern (minus the wildcard), we don't
+        * match
+        */
+       return 0;
+
+   if (strchr(string, '.') < string + lenstr - lenpat)
+
+       /*
+        * If there is a dot left of where the pattern started to match, we
+        * don't match (rule 3)
+        */
+       return 0;
+
+   /* String ended with pattern, and didn't have a dot before, so we match */
+   return 1;
+}
+
+
+/*
+ * Verify that common name resolves to peer.
+ */
+static bool
+verify_peer_name_matches_certificate(PGconn *conn)
+{
+   char       *peer_cn;
+   int         r;
+   int         len;
+   bool        result;
+
+   /*
+    * If told not to verify the peer name, don't do it. Return true
+    * indicating that the verification was successful.
+    */
+   if (strcmp(conn->sslmode, "verify-full") != 0)
+       return true;
+
+   /*
+    * Extract the common name from the certificate.
+    *
+    * XXX: Should support alternate names here
+    */
+   /* First find out the name's length and allocate a buffer for it. */
+   len = X509_NAME_get_text_by_NID(X509_get_subject_name(conn->peer),
+                                   NID_commonName, NULL, 0);
+   if (len == -1)
+   {
+       printfPQExpBuffer(&conn->errorMessage,
+                         libpq_gettext("could not get server common name from server certificate\n"));
+       return false;
+   }
+   peer_cn = malloc(len + 1);
+   if (peer_cn == NULL)
+   {
+       printfPQExpBuffer(&conn->errorMessage,
+                         libpq_gettext("out of memory\n"));
+       return false;
+   }
+
+   r = X509_NAME_get_text_by_NID(X509_get_subject_name(conn->peer),
+                                 NID_commonName, peer_cn, len + 1);
+   if (r != len)
+   {
+       /* Got different length than on the first call. Shouldn't happen. */
+       printfPQExpBuffer(&conn->errorMessage,
+                         libpq_gettext("could not get server common name from server certificate\n"));
+       free(peer_cn);
+       return false;
+   }
+   peer_cn[len] = '\0';
+
+   /*
+    * Reject embedded NULLs in certificate common name to prevent attacks
+    * like CVE-2009-4034.
+    */
+   if (len != strlen(peer_cn))
+   {
+       printfPQExpBuffer(&conn->errorMessage,
+                         libpq_gettext("SSL certificate's common name contains embedded null\n"));
+       free(peer_cn);
+       return false;
+   }
+
+   /*
+    * We got the peer's common name. Now compare it against the originally
+    * given hostname.
+    */
+   if (!(conn->pghost && conn->pghost[0] != '\0'))
+   {
+       printfPQExpBuffer(&conn->errorMessage,
+                         libpq_gettext("host name must be specified for a verified SSL connection\n"));
+       result = false;
+   }
+   else
+   {
+       if (pg_strcasecmp(peer_cn, conn->pghost) == 0)
+           /* Exact name match */
+           result = true;
+       else if (wildcard_certificate_match(peer_cn, conn->pghost))
+           /* Matched wildcard certificate */
+           result = true;
+       else
+       {
+           printfPQExpBuffer(&conn->errorMessage,
+                             libpq_gettext("server common name \"%s\" does not match host name \"%s\"\n"),
+                             peer_cn, conn->pghost);
+           result = false;
+       }
+   }
+
+   free(peer_cn);
+   return result;
+}
+
+#ifdef ENABLE_THREAD_SAFETY
+/*
+ * Callback functions for OpenSSL internal locking
+ */
+
+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();
+}
+
+static pthread_mutex_t *pq_lockarray;
+
+static void
+pq_lockingcallback(int mode, int n, const char *file, int line)
+{
+   if (mode & CRYPTO_LOCK)
+   {
+       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");
+   }
+}
+#endif   /* ENABLE_THREAD_SAFETY */
+
+/*
+ * Initialize SSL system, in particular creating the SSL_context object
+ * that will be shared by all SSL-using connections in this process.
+ *
+ * In threadsafe mode, this includes setting up libcrypto callback functions
+ * to do thread locking.
+ *
+ * If the caller has told us (through PQinitOpenSSL) that he's taking care
+ * of libcrypto, 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 here.
+ *
+ * Returns 0 if OK, -1 on failure (with a message in conn->errorMessage).
+ */
+int
+pgtls_init(PGconn *conn)
+{
+#ifdef ENABLE_THREAD_SAFETY
+#ifdef WIN32
+   /* Also see similar code in fe-connect.c, default_threadlock() */
+   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_init_crypto_lib)
+   {
+       /*
+        * If necessary, set up an array to hold locks for libcrypto.
+        * libcrypto will tell us how big to make this array.
+        */
+       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 libcrypto applications */
+           CRYPTO_set_id_callback(pq_threadidcallback);
+           CRYPTO_set_locking_callback(pq_lockingcallback);
+       }
+   }
+#endif   /* ENABLE_THREAD_SAFETY */
+
+   if (!SSL_context)
+   {
+       if (pq_init_ssl_lib)
+       {
+#if SSLEAY_VERSION_NUMBER >= 0x00907000L
+           OPENSSL_config(NULL);
+#endif
+           SSL_library_init();
+           SSL_load_error_strings();
+       }
+
+       /*
+        * We use SSLv23_method() because it can negotiate use of the highest
+        * mutually supported protocol version, while alternatives like
+        * TLSv1_2_method() permit only one specific version.  Note that we
+        * don't actually allow SSL v2 or v3, only TLS protocols (see below).
+        */
+       SSL_context = SSL_CTX_new(SSLv23_method());
+       if (!SSL_context)
+       {
+           char       *err = SSLerrmessage();
+
+           printfPQExpBuffer(&conn->errorMessage,
+                        libpq_gettext("could not create SSL context: %s\n"),
+                             err);
+           SSLerrfree(err);
+#ifdef ENABLE_THREAD_SAFETY
+           pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+           return -1;
+       }
+
+       /* Disable old protocol versions */
+       SSL_CTX_set_options(SSL_context, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
+
+       /*
+        * Disable OpenSSL's moving-write-buffer sanity check, because it
+        * causes unnecessary failures in nonblocking send cases.
+        */
+       SSL_CTX_set_mode(SSL_context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+   }
+
+#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
+ * 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_init_crypto_lib && ssl_open_connections > 0)
+       --ssl_open_connections;
+
+   if (pq_init_crypto_lib && ssl_open_connections == 0)
+   {
+       /* No connections left, unregister libcrypto callbacks */
+       CRYPTO_set_locking_callback(NULL);
+       CRYPTO_set_id_callback(NULL);
+
+       /*
+        * 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.
+        */
+   }
+
+   pthread_mutex_unlock(&ssl_config_mutex);
+#endif
+}
+
+/*
+ * Initialize (potentially) per-connection SSL data, namely the
+ * client certificate, private key, and trusted CA certs.
+ *
+ * conn->ssl must already be created.  It receives the connection's client
+ * certificate and private key.  Note however that certificates also get
+ * loaded into the SSL_context object, and are therefore accessible to all
+ * connections in this process.  This should be OK as long as there aren't
+ * any hash collisions among the certs.
+ *
+ * Returns 0 if OK, -1 on failure (with a message in conn->errorMessage).
+ */
+static int
+initialize_SSL(PGconn *conn)
+{
+   struct stat buf;
+   char        homedir[MAXPGPATH];
+   char        fnbuf[MAXPGPATH];
+   char        sebuf[256];
+   bool        have_homedir;
+   bool        have_cert;
+   EVP_PKEY   *pkey = NULL;
+
+   /*
+    * We'll need the home directory if any of the relevant parameters are
+    * defaulted.  If pqGetHomeDirectory fails, act as though none of the
+    * files could be found.
+    */
+   if (!(conn->sslcert && strlen(conn->sslcert) > 0) ||
+       !(conn->sslkey && strlen(conn->sslkey) > 0) ||
+       !(conn->sslrootcert && strlen(conn->sslrootcert) > 0) ||
+       !(conn->sslcrl && strlen(conn->sslcrl) > 0))
+       have_homedir = pqGetHomeDirectory(homedir, sizeof(homedir));
+   else    /* won't need it */
+       have_homedir = false;
+
+   /* Read the client certificate file */
+   if (conn->sslcert && strlen(conn->sslcert) > 0)
+       strncpy(fnbuf, conn->sslcert, sizeof(fnbuf));
+   else if (have_homedir)
+       snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_CERT_FILE);
+   else
+       fnbuf[0] = '\0';
+
+   if (fnbuf[0] == '\0')
+   {
+       /* no home directory, proceed without a client cert */
+       have_cert = false;
+   }
+   else if (stat(fnbuf, &buf) != 0)
+   {
+       /*
+        * If file is not present, just go on without a client cert; server
+        * might or might not accept the connection.  Any other error,
+        * however, is grounds for complaint.
+        */
+       if (errno != ENOENT && errno != ENOTDIR)
+       {
+           printfPQExpBuffer(&conn->errorMessage,
+              libpq_gettext("could not open certificate file \"%s\": %s\n"),
+                             fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
+           return -1;
+       }
+       have_cert = false;
+   }
+   else
+   {
+       /*
+        * Cert file exists, so load it.  Since OpenSSL doesn't provide the
+        * equivalent of "SSL_use_certificate_chain_file", we actually have to
+        * load the file twice.  The first call loads any extra certs after
+        * the first one into chain-cert storage associated with the
+        * SSL_context.  The second call loads the first cert (only) into the
+        * SSL object, where it will be correctly paired with the private key
+        * we load below.  We do it this way so that each connection
+        * 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
+       int         rc;
+
+       if ((rc = pthread_mutex_lock(&ssl_config_mutex)))
+       {
+           printfPQExpBuffer(&conn->errorMessage,
+              libpq_gettext("could not acquire mutex: %s\n"), strerror(rc));
+           return -1;
+       }
+#endif
+       if (SSL_CTX_use_certificate_chain_file(SSL_context, fnbuf) != 1)
+       {
+           char       *err = SSLerrmessage();
+
+           printfPQExpBuffer(&conn->errorMessage,
+              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();
+
+           printfPQExpBuffer(&conn->errorMessage,
+              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
+   }
+
+   /*
+    * Read the SSL key. If a key is specified, treat it as an engine:key
+    * combination if there is colon present - we don't support files with
+    * colon in the name. The exception is if the second character is a colon,
+    * in which case it can be a Windows filename with drive specification.
+    */
+   if (have_cert && conn->sslkey && strlen(conn->sslkey) > 0)
+   {
+#ifdef USE_SSL_ENGINE
+       if (strchr(conn->sslkey, ':')
+#ifdef WIN32
+           && conn->sslkey[1] != ':'
+#endif
+           )
+       {
+           /* Colon, but not in second character, treat as engine:key */
+           char       *engine_str = strdup(conn->sslkey);
+           char       *engine_colon;
+
+           if (engine_str == NULL)
+           {
+               printfPQExpBuffer(&conn->errorMessage,
+                                 libpq_gettext("out of memory\n"));
+               return -1;
+           }
+
+           /* cannot return NULL because we already checked before strdup */
+           engine_colon = strchr(engine_str, ':');
+
+           *engine_colon = '\0';       /* engine_str now has engine name */
+           engine_colon++;     /* engine_colon now has key name */
+
+           conn->engine = ENGINE_by_id(engine_str);
+           if (conn->engine == NULL)
+           {
+               char       *err = SSLerrmessage();
+
+               printfPQExpBuffer(&conn->errorMessage,
+                    libpq_gettext("could not load SSL engine \"%s\": %s\n"),
+                                 engine_str, err);
+               SSLerrfree(err);
+               free(engine_str);
+               return -1;
+           }
+
+           if (ENGINE_init(conn->engine) == 0)
+           {
+               char       *err = SSLerrmessage();
+
+               printfPQExpBuffer(&conn->errorMessage,
+               libpq_gettext("could not initialize SSL engine \"%s\": %s\n"),
+                                 engine_str, err);
+               SSLerrfree(err);
+               ENGINE_free(conn->engine);
+               conn->engine = NULL;
+               free(engine_str);
+               return -1;
+           }
+
+           pkey = ENGINE_load_private_key(conn->engine, engine_colon,
+                                          NULL, NULL);
+           if (pkey == NULL)
+           {
+               char       *err = SSLerrmessage();
+
+               printfPQExpBuffer(&conn->errorMessage,
+                                 libpq_gettext("could not read private SSL key \"%s\" from engine \"%s\": %s\n"),
+                                 engine_colon, engine_str, err);
+               SSLerrfree(err);
+               ENGINE_finish(conn->engine);
+               ENGINE_free(conn->engine);
+               conn->engine = NULL;
+               free(engine_str);
+               return -1;
+           }
+           if (SSL_use_PrivateKey(conn->ssl, pkey) != 1)
+           {
+               char       *err = SSLerrmessage();
+
+               printfPQExpBuffer(&conn->errorMessage,
+                                 libpq_gettext("could not load private SSL key \"%s\" from engine \"%s\": %s\n"),
+                                 engine_colon, engine_str, err);
+               SSLerrfree(err);
+               ENGINE_finish(conn->engine);
+               ENGINE_free(conn->engine);
+               conn->engine = NULL;
+               free(engine_str);
+               return -1;
+           }
+
+           free(engine_str);
+
+           fnbuf[0] = '\0';    /* indicate we're not going to load from a
+                                * file */
+       }
+       else
+#endif   /* USE_SSL_ENGINE */
+       {
+           /* PGSSLKEY is not an engine, treat it as a filename */
+           strncpy(fnbuf, conn->sslkey, sizeof(fnbuf));
+       }
+   }
+   else if (have_homedir)
+   {
+       /* No PGSSLKEY specified, load default file */
+       snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_KEY_FILE);
+   }
+   else
+       fnbuf[0] = '\0';
+
+   if (have_cert && fnbuf[0] != '\0')
+   {
+       /* read the client key from file */
+
+       if (stat(fnbuf, &buf) != 0)
+       {
+           printfPQExpBuffer(&conn->errorMessage,
+                             libpq_gettext("certificate present, but not private key file \"%s\"\n"),
+                             fnbuf);
+           return -1;
+       }
+#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);
+           return -1;
+       }
+#endif
+
+       if (SSL_use_PrivateKey_file(conn->ssl, fnbuf, SSL_FILETYPE_PEM) != 1)
+       {
+           char       *err = SSLerrmessage();
+
+           printfPQExpBuffer(&conn->errorMessage,
+              libpq_gettext("could not load private key file \"%s\": %s\n"),
+                             fnbuf, err);
+           SSLerrfree(err);
+           return -1;
+       }
+   }
+
+   /* verify that the cert and key go together */
+   if (have_cert &&
+       SSL_check_private_key(conn->ssl) != 1)
+   {
+       char       *err = SSLerrmessage();
+
+       printfPQExpBuffer(&conn->errorMessage,
+                         libpq_gettext("certificate does not match private key file \"%s\": %s\n"),
+                         fnbuf, err);
+       SSLerrfree(err);
+       return -1;
+   }
+
+   /*
+    * If the root cert file exists, load it so we can perform certificate
+    * verification. If sslmode is "verify-full" we will also do further
+    * verification after the connection has been completed.
+    */
+   if (conn->sslrootcert && strlen(conn->sslrootcert) > 0)
+       strncpy(fnbuf, conn->sslrootcert, sizeof(fnbuf));
+   else if (have_homedir)
+       snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CERT_FILE);
+   else
+       fnbuf[0] = '\0';
+
+   if (fnbuf[0] != '\0' &&
+       stat(fnbuf, &buf) == 0)
+   {
+       X509_STORE *cvstore;
+
+#ifdef ENABLE_THREAD_SAFETY
+       int         rc;
+
+       if ((rc = pthread_mutex_lock(&ssl_config_mutex)))
+       {
+           printfPQExpBuffer(&conn->errorMessage,
+              libpq_gettext("could not acquire mutex: %s\n"), strerror(rc));
+           return -1;
+       }
+#endif
+       if (SSL_CTX_load_verify_locations(SSL_context, fnbuf, NULL) != 1)
+       {
+           char       *err = SSLerrmessage();
+
+           printfPQExpBuffer(&conn->errorMessage,
+                             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;
+       }
+
+       if ((cvstore = SSL_CTX_get_cert_store(SSL_context)) != NULL)
+       {
+           if (conn->sslcrl && strlen(conn->sslcrl) > 0)
+               strncpy(fnbuf, conn->sslcrl, sizeof(fnbuf));
+           else if (have_homedir)
+               snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CRL_FILE);
+           else
+               fnbuf[0] = '\0';
+
+           /* Set the flags to check against the complete CRL chain */
+           if (fnbuf[0] != '\0' &&
+               X509_STORE_load_locations(cvstore, fnbuf, NULL) == 1)
+           {
+               /* 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);
+#else
+               char       *err = SSLerrmessage();
+
+               printfPQExpBuffer(&conn->errorMessage,
+                                 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);
+   }
+   else
+   {
+       /*
+        * stat() failed; assume root file doesn't exist.  If sslmode is
+        * verify-ca or verify-full, this is an error.  Otherwise, continue
+        * without performing any server cert verification.
+        */
+       if (conn->sslmode[0] == 'v')    /* "verify-ca" or "verify-full" */
+       {
+           /*
+            * The only way to reach here with an empty filename is if
+            * pqGetHomeDirectory failed.  That's a sufficiently unusual case
+            * that it seems worth having a specialized error message for it.
+            */
+           if (fnbuf[0] == '\0')
+               printfPQExpBuffer(&conn->errorMessage,
+                                 libpq_gettext("could not get home directory to locate root certificate file\n"
+                                               "Either provide the file or change sslmode to disable server certificate verification.\n"));
+           else
+               printfPQExpBuffer(&conn->errorMessage,
+               libpq_gettext("root certificate file \"%s\" does not exist\n"
+                             "Either provide the file or change sslmode to disable server certificate verification.\n"), fnbuf);
+           return -1;
+       }
+   }
+
+   /*
+    * If the OpenSSL version used supports it (from 1.0.0 on) and the user
+    * requested it, disable SSL compression.
+    */
+#ifdef SSL_OP_NO_COMPRESSION
+   if (conn->sslcompression && conn->sslcompression[0] == '0')
+   {
+       SSL_set_options(conn->ssl, SSL_OP_NO_COMPRESSION);
+   }
+#endif
+
+   return 0;
+}
+
+/*
+ * Attempt to negotiate SSL connection.
+ */
+static PostgresPollingStatusType
+open_client_SSL(PGconn *conn)
+{
+   int         r;
+
+   r = SSL_connect(conn->ssl);
+   if (r <= 0)
+   {
+       int         err = SSL_get_error(conn->ssl, r);
+
+       switch (err)
+       {
+           case SSL_ERROR_WANT_READ:
+               return PGRES_POLLING_READING;
+
+           case SSL_ERROR_WANT_WRITE:
+               return PGRES_POLLING_WRITING;
+
+           case SSL_ERROR_SYSCALL:
+               {
+                   char        sebuf[256];
+
+                   if (r == -1)
+                       printfPQExpBuffer(&conn->errorMessage,
+                                   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"));
+                   pgtls_close(conn);
+                   return PGRES_POLLING_FAILED;
+               }
+           case SSL_ERROR_SSL:
+               {
+                   char       *err = SSLerrmessage();
+
+                   printfPQExpBuffer(&conn->errorMessage,
+                                     libpq_gettext("SSL error: %s\n"),
+                                     err);
+                   SSLerrfree(err);
+                   pgtls_close(conn);
+                   return PGRES_POLLING_FAILED;
+               }
+
+           default:
+               printfPQExpBuffer(&conn->errorMessage,
+                         libpq_gettext("unrecognized SSL error code: %d\n"),
+                                 err);
+               pgtls_close(conn);
+               return PGRES_POLLING_FAILED;
+       }
+   }
+
+   /*
+    * We already checked the server certificate in initialize_SSL() using
+    * SSL_CTX_set_verify(), if root.crt exists.
+    */
+
+   /* get server certificate */
+   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"),
+                         err);
+       SSLerrfree(err);
+       pgtls_close(conn);
+       return PGRES_POLLING_FAILED;
+   }
+
+   if (!verify_peer_name_matches_certificate(conn))
+   {
+       pgtls_close(conn);
+       return PGRES_POLLING_FAILED;
+   }
+
+   /* SSL handshake is complete */
+   return PGRES_POLLING_OK;
+}
+
+/*
+ * Close SSL connection.
+ */
+void
+pgtls_close(PGconn *conn)
+{
+   bool        destroy_needed = false;
+
+   if (conn->ssl)
+   {
+       /*
+        * We can't destroy everything SSL-related here due to the possible
+        * later calls to OpenSSL routines which may need our thread
+        * callbacks, so set a flag here and check at the end.
+        */
+       destroy_needed = true;
+
+       SSL_shutdown(conn->ssl);
+       SSL_free(conn->ssl);
+       conn->ssl = NULL;
+       conn->ssl_in_use = false;
+   }
+
+   if (conn->peer)
+   {
+       X509_free(conn->peer);
+       conn->peer = NULL;
+   }
+
+#ifdef USE_SSL_ENGINE
+   if (conn->engine)
+   {
+       ENGINE_finish(conn->engine);
+       ENGINE_free(conn->engine);
+       conn->engine = NULL;
+   }
+#endif
+
+   /*
+    * This will remove our SSL locking hooks, if this is the last SSL
+    * connection, which means we must wait to call it until after all SSL
+    * calls have been made, otherwise we can end up with a race condition and
+    * possible deadlocks.
+    *
+    * See comments above destroy_ssl_system().
+    */
+   if (destroy_needed)
+       destroy_ssl_system();
+}
+
+
+/*
+ * Obtain reason string for last SSL error
+ *
+ * Some caution is needed here since ERR_reason_error_string will
+ * return NULL if it doesn't recognize the error code.  We don't
+ * want to return NULL ever.
+ */
+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;
+   char       *errbuf;
+
+   errbuf = malloc(SSL_ERR_LEN);
+   if (!errbuf)
+       return ssl_nomem;
+   errcode = ERR_get_error();
+   if (errcode == 0)
+   {
+       snprintf(errbuf, SSL_ERR_LEN, libpq_gettext("no SSL error reported"));
+       return errbuf;
+   }
+   errreason = ERR_reason_error_string(errcode);
+   if (errreason != NULL)
+   {
+       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 OpenSSL object.
+ */
+void *
+PQgetssl(PGconn *conn)
+{
+   if (!conn)
+       return NULL;
+   return conn->ssl;
+}
+
+
+/*
+ * Private substitute BIO: this does the sending and receiving using send() and
+ * recv() instead. This is so that we can enable and disable interrupts
+ * just while calling recv(). We cannot have interrupts occurring while
+ * the bulk of openssl runs, because it uses malloc() and possibly other
+ * non-reentrant libc facilities. We also need to call send() and recv()
+ * directly so it gets passed through the socket/signals layer on Win32.
+ *
+ * These functions are closely modelled on the standard socket BIO in OpenSSL;
+ * see sock_read() and sock_write() in OpenSSL's crypto/bio/bss_sock.c.
+ * XXX OpenSSL 1.0.1e considers many more errcodes than just EINTR as reasons
+ * to retry; do we need to adopt their logic for that?
+ */
+
+static bool my_bio_initialized = false;
+static BIO_METHOD my_bio_methods;
+
+static int
+my_sock_read(BIO *h, char *buf, int size)
+{
+   int         res;
+   int         save_errno;
+
+   res = pqsecure_raw_read((PGconn *) h->ptr, buf, size);
+   save_errno = errno;
+   BIO_clear_retry_flags(h);
+   if (res < 0)
+   {
+       switch (save_errno)
+       {
+#ifdef EAGAIN
+           case EAGAIN:
+#endif
+#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
+           case EWOULDBLOCK:
+#endif
+           case EINTR:
+               BIO_set_retry_read(h);
+               break;
+
+           default:
+               break;
+       }
+   }
+
+   errno = save_errno;
+   return res;
+}
+
+static int
+my_sock_write(BIO *h, const char *buf, int size)
+{
+   int         res;
+   int         save_errno;
+
+   res = pqsecure_raw_write((PGconn *) h->ptr, buf, size);
+   save_errno = errno;
+   BIO_clear_retry_flags(h);
+   if (res <= 0)
+   {
+       if (save_errno == EINTR)
+       {
+           BIO_set_retry_write(h);
+       }
+   }
+
+   return res;
+}
+
+static BIO_METHOD *
+my_BIO_s_socket(void)
+{
+   if (!my_bio_initialized)
+   {
+       memcpy(&my_bio_methods, BIO_s_socket(), sizeof(BIO_METHOD));
+       my_bio_methods.bread = my_sock_read;
+       my_bio_methods.bwrite = my_sock_write;
+       my_bio_initialized = true;
+   }
+   return &my_bio_methods;
+}
+
+/* This should exactly match openssl's SSL_set_fd except for using my BIO */
+static int
+my_SSL_set_fd(PGconn *conn, int fd)
+{
+   int         ret = 0;
+   BIO        *bio = NULL;
+
+   bio = BIO_new(my_BIO_s_socket());
+   if (bio == NULL)
+   {
+       SSLerr(SSL_F_SSL_SET_FD, ERR_R_BUF_LIB);
+       goto err;
+   }
+   /* Use 'ptr' to store pointer to PGconn */
+   bio->ptr = conn;
+
+   SSL_set_bio(conn->ssl, bio, bio);
+   BIO_set_fd(bio, fd, BIO_NOCLOSE);
+   ret = 1;
+err:
+   return ret;
+}
index 9ba35674d38c0e7b9581db4de6b150be5f5cb639..66778b24948eb3c1c5914e331254b300b2037c72 100644 (file)
 #endif
 #endif
 
-#ifdef USE_SSL
-
-#include <openssl/ssl.h>
-#if (SSLEAY_VERSION_NUMBER >= 0x00907000L)
-#include <openssl/conf.h>
-#endif
-#ifdef USE_SSL_ENGINE
-#include <openssl/engine.h>
-#endif
-
-
-#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
-
-static bool verify_peer_name_matches_certificate(PGconn *);
-static int verify_cb(int ok, X509_STORE_CTX *ctx);
-static int init_ssl_system(PGconn *conn);
-static void destroy_ssl_system(void);
-static int initialize_SSL(PGconn *conn);
-static void destroySSL(void);
-static PostgresPollingStatusType open_client_SSL(PGconn *);
-static void close_SSL(PGconn *);
-static char *SSLerrmessage(void);
-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
-static long 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.
  * On Windows, these are all no-ops since there's no SIGPIPEs.
@@ -194,7 +136,9 @@ struct sigpipe_info
 void
 PQinitSSL(int do_init)
 {
-   PQinitOpenSSL(do_init, do_init);
+#ifdef USE_SSL
+   pgtls_init_library(do_init, do_init);
+#endif
 }
 
 /*
@@ -205,18 +149,7 @@ void
 PQinitOpenSSL(int do_ssl, int do_crypto)
 {
 #ifdef USE_SSL
-#ifdef ENABLE_THREAD_SAFETY
-
-   /*
-    * Disallow changing the flags while we have open connections, else we'd
-    * get completely confused.
-    */
-   if (ssl_open_connections != 0)
-       return;
-#endif
-
-   pq_init_ssl_lib = do_ssl;
-   pq_init_crypto_lib = do_crypto;
+   pgtls_init_library(do_ssl, do_crypto);
 #endif
 }
 
@@ -229,23 +162,12 @@ pqsecure_initialize(PGconn *conn)
    int         r = 0;
 
 #ifdef USE_SSL
-   r = init_ssl_system(conn);
+   r = pgtls_init(conn);
 #endif
 
    return r;
 }
 
-/*
- * Destroy global context
- */
-void
-pqsecure_destroy(void)
-{
-#ifdef USE_SSL
-   destroySSL();
-#endif
-}
-
 /*
  * Begin or continue negotiating a secure session.
  */
@@ -253,59 +175,7 @@ PostgresPollingStatusType
 pqsecure_open_client(PGconn *conn)
 {
 #ifdef USE_SSL
-   /* First time through? */
-   if (conn->ssl == NULL)
-   {
-#ifdef ENABLE_THREAD_SAFETY
-       int         rc;
-#endif
-
-       /* We cannot use MSG_NOSIGNAL to block SIGPIPE when using SSL */
-       conn->sigpipe_flag = false;
-
-#ifdef ENABLE_THREAD_SAFETY
-       if ((rc = pthread_mutex_lock(&ssl_config_mutex)))
-       {
-           printfPQExpBuffer(&conn->errorMessage,
-              libpq_gettext("could not acquire mutex: %s\n"), strerror(rc));
-           return PGRES_POLLING_FAILED;
-       }
-#endif
-       /* Create a connection-specific SSL object */
-       if (!(conn->ssl = SSL_new(SSL_context)) ||
-           !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"),
-                             err);
-           SSLerrfree(err);
-#ifdef ENABLE_THREAD_SAFETY
-           pthread_mutex_unlock(&ssl_config_mutex);
-#endif
-           close_SSL(conn);
-
-           return PGRES_POLLING_FAILED;
-       }
-#ifdef ENABLE_THREAD_SAFETY
-       pthread_mutex_unlock(&ssl_config_mutex);
-#endif
-
-       /*
-        * Load client certificate, private key, and trusted CA certs.
-        */
-       if (initialize_SSL(conn) != 0)
-       {
-           /* initialize_SSL already put a message in conn->errorMessage */
-           close_SSL(conn);
-           return PGRES_POLLING_FAILED;
-       }
-   }
-
-   /* Begin or continue the actual handshake */
-   return open_client_SSL(conn);
+   return pgtls_open_client(conn);
 #else
    /* shouldn't get here */
    return PGRES_POLLING_FAILED;
@@ -319,8 +189,8 @@ void
 pqsecure_close(PGconn *conn)
 {
 #ifdef USE_SSL
-   if (conn->ssl)
-       close_SSL(conn);
+   if (conn->ssl_in_use)
+       pgtls_close(conn);
 #endif
 }
 
@@ -335,149 +205,63 @@ ssize_t
 pqsecure_read(PGconn *conn, void *ptr, size_t len)
 {
    ssize_t     n;
-   int         result_errno = 0;
-   char        sebuf[256];
 
 #ifdef USE_SSL
-   if (conn->ssl)
+   if (conn->ssl_in_use)
+   {
+       n = pgtls_read(conn, ptr, len);
+   }
+   else
+#endif
    {
-       int         err;
+       n = pqsecure_raw_read(conn, ptr, len);
+   }
 
-       DECLARE_SIGPIPE_INFO(spinfo);
+   return n;
+}
 
-       /* SSL_read can write to the socket, so we need to disable SIGPIPE */
-       DISABLE_SIGPIPE(conn, spinfo, return -1);
+ssize_t
+pqsecure_raw_read(PGconn *conn, void *ptr, size_t len)
+{
+   ssize_t     n;
+   int         result_errno = 0;
+   char        sebuf[256];
 
-rloop:
-       SOCK_ERRNO_SET(0);
-       n = SSL_read(conn->ssl, ptr, len);
-       err = SSL_get_error(conn->ssl, n);
-       switch (err)
-       {
-           case SSL_ERROR_NONE:
-               if (n < 0)
-               {
-                   /* Not supposed to happen, so we don't translate the msg */
-                   printfPQExpBuffer(&conn->errorMessage,
-                                     "SSL_read failed but did not provide error information\n");
-                   /* assume the connection is broken */
-                   result_errno = ECONNRESET;
-               }
-               break;
-           case SSL_ERROR_WANT_READ:
-               n = 0;
-               break;
-           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.
-                */
-               goto rloop;
-           case SSL_ERROR_SYSCALL:
-               if (n < 0)
-               {
-                   result_errno = SOCK_ERRNO;
-                   REMEMBER_EPIPE(spinfo, result_errno == EPIPE);
-                   if (result_errno == EPIPE ||
-                       result_errno == ECONNRESET)
-                       printfPQExpBuffer(&conn->errorMessage,
-                                         libpq_gettext(
-                               "server closed the connection unexpectedly\n"
-                                                       "\tThis probably means the server terminated abnormally\n"
-                            "\tbefore or while processing the request.\n"));
-                   else
-                       printfPQExpBuffer(&conn->errorMessage,
-                                   libpq_gettext("SSL SYSCALL error: %s\n"),
-                                         SOCK_STRERROR(result_errno,
-                                                     sebuf, sizeof(sebuf)));
-               }
-               else
-               {
-                   printfPQExpBuffer(&conn->errorMessage,
-                        libpq_gettext("SSL SYSCALL error: EOF detected\n"));
-                   /* assume the connection is broken */
-                   result_errno = ECONNRESET;
-                   n = -1;
-               }
-               break;
-           case SSL_ERROR_SSL:
-               {
-                   char       *errm = SSLerrmessage();
-
-                   printfPQExpBuffer(&conn->errorMessage,
-                                     libpq_gettext("SSL error: %s\n"), errm);
-                   SSLerrfree(errm);
-                   /* assume the connection is broken */
-                   result_errno = ECONNRESET;
-                   n = -1;
-                   break;
-               }
-           case SSL_ERROR_ZERO_RETURN:
-
-               /*
-                * Per OpenSSL documentation, this error code is only returned
-                * for a clean connection closure, so we should not report it
-                * as a server crash.
-                */
-               printfPQExpBuffer(&conn->errorMessage,
-                                 libpq_gettext("SSL connection has been closed unexpectedly\n"));
-               result_errno = ECONNRESET;
-               n = -1;
-               break;
-           default:
-               printfPQExpBuffer(&conn->errorMessage,
-                         libpq_gettext("unrecognized SSL error code: %d\n"),
-                                 err);
-               /* assume the connection is broken */
-               result_errno = ECONNRESET;
-               n = -1;
-               break;
-       }
+   n = recv(conn->sock, ptr, len, 0);
 
-       RESTORE_SIGPIPE(conn, spinfo);
-   }
-   else
-#endif   /* USE_SSL */
+   if (n < 0)
    {
-       n = recv(conn->sock, ptr, len, 0);
+       result_errno = SOCK_ERRNO;
 
-       if (n < 0)
+       /* Set error message if appropriate */
+       switch (result_errno)
        {
-           result_errno = SOCK_ERRNO;
-
-           /* Set error message if appropriate */
-           switch (result_errno)
-           {
 #ifdef EAGAIN
-               case EAGAIN:
+           case EAGAIN:
 #endif
 #if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
-               case EWOULDBLOCK:
+           case EWOULDBLOCK:
 #endif
-               case EINTR:
-                   /* no error message, caller is expected to retry */
-                   break;
+           case EINTR:
+               /* no error message, caller is expected to retry */
+               break;
 
 #ifdef ECONNRESET
-               case ECONNRESET:
-                   printfPQExpBuffer(&conn->errorMessage,
-                                     libpq_gettext(
+           case ECONNRESET:
+               printfPQExpBuffer(&conn->errorMessage,
+                                 libpq_gettext(
                                "server closed the connection unexpectedly\n"
                    "\tThis probably means the server terminated abnormally\n"
                             "\tbefore or while processing the request.\n"));
-                   break;
+               break;
 #endif
 
-               default:
-                   printfPQExpBuffer(&conn->errorMessage,
+           default:
+               printfPQExpBuffer(&conn->errorMessage,
                    libpq_gettext("could not receive data from server: %s\n"),
-                                     SOCK_STRERROR(result_errno,
-                                                   sebuf, sizeof(sebuf)));
-                   break;
-           }
+                                 SOCK_STRERROR(result_errno,
+                                               sebuf, sizeof(sebuf)));
+               break;
        }
    }
 
@@ -498,175 +282,94 @@ ssize_t
 pqsecure_write(PGconn *conn, const void *ptr, size_t len)
 {
    ssize_t     n;
-   int         result_errno = 0;
-   char        sebuf[256];
-
-   DECLARE_SIGPIPE_INFO(spinfo);
 
 #ifdef USE_SSL
-   if (conn->ssl)
+   if (conn->ssl_in_use)
    {
-       int         err;
-
-       DISABLE_SIGPIPE(conn, spinfo, return -1);
-
-       SOCK_ERRNO_SET(0);
-       n = SSL_write(conn->ssl, ptr, len);
-       err = SSL_get_error(conn->ssl, n);
-       switch (err)
-       {
-           case SSL_ERROR_NONE:
-               if (n < 0)
-               {
-                   /* Not supposed to happen, so we don't translate the msg */
-                   printfPQExpBuffer(&conn->errorMessage,
-                                     "SSL_write failed but did not provide error information\n");
-                   /* assume the connection is broken */
-                   result_errno = ECONNRESET;
-               }
-               break;
-           case SSL_ERROR_WANT_READ:
-
-               /*
-                * 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.
-                */
-               n = 0;
-               break;
-           case SSL_ERROR_WANT_WRITE:
-               n = 0;
-               break;
-           case SSL_ERROR_SYSCALL:
-               if (n < 0)
-               {
-                   result_errno = SOCK_ERRNO;
-                   REMEMBER_EPIPE(spinfo, result_errno == EPIPE);
-                   if (result_errno == EPIPE ||
-                       result_errno == ECONNRESET)
-                       printfPQExpBuffer(&conn->errorMessage,
-                                         libpq_gettext(
-                               "server closed the connection unexpectedly\n"
-                                                       "\tThis probably means the server terminated abnormally\n"
-                            "\tbefore or while processing the request.\n"));
-                   else
-                       printfPQExpBuffer(&conn->errorMessage,
-                                   libpq_gettext("SSL SYSCALL error: %s\n"),
-                                         SOCK_STRERROR(result_errno,
-                                                     sebuf, sizeof(sebuf)));
-               }
-               else
-               {
-                   printfPQExpBuffer(&conn->errorMessage,
-                        libpq_gettext("SSL SYSCALL error: EOF detected\n"));
-                   /* assume the connection is broken */
-                   result_errno = ECONNRESET;
-                   n = -1;
-               }
-               break;
-           case SSL_ERROR_SSL:
-               {
-                   char       *errm = SSLerrmessage();
-
-                   printfPQExpBuffer(&conn->errorMessage,
-                                     libpq_gettext("SSL error: %s\n"), errm);
-                   SSLerrfree(errm);
-                   /* assume the connection is broken */
-                   result_errno = ECONNRESET;
-                   n = -1;
-                   break;
-               }
-           case SSL_ERROR_ZERO_RETURN:
-
-               /*
-                * Per OpenSSL documentation, this error code is only returned
-                * for a clean connection closure, so we should not report it
-                * as a server crash.
-                */
-               printfPQExpBuffer(&conn->errorMessage,
-                                 libpq_gettext("SSL connection has been closed unexpectedly\n"));
-               result_errno = ECONNRESET;
-               n = -1;
-               break;
-           default:
-               printfPQExpBuffer(&conn->errorMessage,
-                         libpq_gettext("unrecognized SSL error code: %d\n"),
-                                 err);
-               /* assume the connection is broken */
-               result_errno = ECONNRESET;
-               n = -1;
-               break;
-       }
+       n = pgtls_write(conn, ptr, len);
    }
    else
-#endif   /* USE_SSL */
+#endif
    {
-       int         flags = 0;
+       n = pqsecure_raw_write(conn, ptr, len);
+   }
+
+   return n;
+}
+
+ssize_t
+pqsecure_raw_write(PGconn *conn, const void *ptr, size_t len)
+{
+   ssize_t     n;
+   int         flags = 0;
+   int         result_errno = 0;
+   char        sebuf[256];
+
+   DECLARE_SIGPIPE_INFO(spinfo);
 
 #ifdef MSG_NOSIGNAL
-       if (conn->sigpipe_flag)
-           flags |= MSG_NOSIGNAL;
+   if (conn->sigpipe_flag)
+       flags |= MSG_NOSIGNAL;
 
 retry_masked:
 #endif   /* MSG_NOSIGNAL */
 
-       DISABLE_SIGPIPE(conn, spinfo, return -1);
+   DISABLE_SIGPIPE(conn, spinfo, return -1);
 
-       n = send(conn->sock, ptr, len, flags);
+   n = send(conn->sock, ptr, len, flags);
 
-       if (n < 0)
-       {
-           result_errno = SOCK_ERRNO;
+   if (n < 0)
+   {
+       result_errno = SOCK_ERRNO;
 
-           /*
-            * If we see an EINVAL, it may be because MSG_NOSIGNAL isn't
-            * available on this machine.  So, clear sigpipe_flag so we don't
-            * try the flag again, and retry the send().
-            */
+       /*
+        * If we see an EINVAL, it may be because MSG_NOSIGNAL isn't
+        * available on this machine.  So, clear sigpipe_flag so we don't
+        * try the flag again, and retry the send().
+        */
 #ifdef MSG_NOSIGNAL
-           if (flags != 0 && result_errno == EINVAL)
-           {
-               conn->sigpipe_flag = false;
-               flags = 0;
-               goto retry_masked;
-           }
+       if (flags != 0 && result_errno == EINVAL)
+       {
+           conn->sigpipe_flag = false;
+           flags = 0;
+           goto retry_masked;
+       }
 #endif   /* MSG_NOSIGNAL */
 
-           /* Set error message if appropriate */
-           switch (result_errno)
-           {
+       /* Set error message if appropriate */
+       switch (result_errno)
+       {
 #ifdef EAGAIN
-               case EAGAIN:
+           case EAGAIN:
 #endif
 #if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
-               case EWOULDBLOCK:
+           case EWOULDBLOCK:
 #endif
-               case EINTR:
-                   /* no error message, caller is expected to retry */
-                   break;
+           case EINTR:
+               /* no error message, caller is expected to retry */
+               break;
 
-               case EPIPE:
-                   /* Set flag for EPIPE */
-                   REMEMBER_EPIPE(spinfo, true);
-                   /* FALL THRU */
+           case EPIPE:
+               /* Set flag for EPIPE */
+               REMEMBER_EPIPE(spinfo, true);
+               /* FALL THRU */
 
 #ifdef ECONNRESET
-               case ECONNRESET:
+           case ECONNRESET:
 #endif
-                   printfPQExpBuffer(&conn->errorMessage,
-                                     libpq_gettext(
+               printfPQExpBuffer(&conn->errorMessage,
+                                 libpq_gettext(
                                "server closed the connection unexpectedly\n"
                    "\tThis probably means the server terminated abnormally\n"
                             "\tbefore or while processing the request.\n"));
-                   break;
+               break;
 
-               default:
-                   printfPQExpBuffer(&conn->errorMessage,
+           default:
+               printfPQExpBuffer(&conn->errorMessage,
                        libpq_gettext("could not send data to server: %s\n"),
                                      SOCK_STRERROR(result_errno,
                                                    sebuf, sizeof(sebuf)));
-                   break;
-           }
+               break;
        }
    }
 
@@ -678,981 +381,7 @@ retry_masked:
    return n;
 }
 
-/* ------------------------------------------------------------ */
-/*                       SSL specific code                     */
-/* ------------------------------------------------------------ */
-#ifdef USE_SSL
-
-/*
- * Certificate verification callback
- *
- * This callback allows us to log intermediate problems during
- * verification, but there doesn't seem to be a clean way to get
- * our PGconn * structure.  So we can't log anything!
- *
- * This callback also allows us to override the default acceptance
- * criteria (e.g., accepting self-signed or expired certs), but
- * for now we accept the default checks.
- */
-static int
-verify_cb(int ok, X509_STORE_CTX *ctx)
-{
-   return ok;
-}
-
-
-/*
- * Check if a wildcard certificate matches the server hostname.
- *
- * The rule for this is:
- * 1. We only match the '*' character as wildcard
- * 2. We match only wildcards at the start of the string
- * 3. The '*' character does *not* match '.', meaning that we match only
- *    a single pathname component.
- * 4. We don't support more than one '*' in a single pattern.
- *
- * This is roughly in line with RFC2818, but contrary to what most browsers
- * appear to be implementing (point 3 being the difference)
- *
- * Matching is always case-insensitive, since DNS is case insensitive.
- */
-static int
-wildcard_certificate_match(const char *pattern, const char *string)
-{
-   int         lenpat = strlen(pattern);
-   int         lenstr = strlen(string);
-
-   /* If we don't start with a wildcard, it's not a match (rule 1 & 2) */
-   if (lenpat < 3 ||
-       pattern[0] != '*' ||
-       pattern[1] != '.')
-       return 0;
-
-   if (lenpat > lenstr)
-       /* If pattern is longer than the string, we can never match */
-       return 0;
-
-   if (pg_strcasecmp(pattern + 1, string + lenstr - lenpat + 1) != 0)
-
-       /*
-        * If string does not end in pattern (minus the wildcard), we don't
-        * match
-        */
-       return 0;
-
-   if (strchr(string, '.') < string + lenstr - lenpat)
-
-       /*
-        * If there is a dot left of where the pattern started to match, we
-        * don't match (rule 3)
-        */
-       return 0;
-
-   /* String ended with pattern, and didn't have a dot before, so we match */
-   return 1;
-}
-
-
-/*
- * Verify that common name resolves to peer.
- */
-static bool
-verify_peer_name_matches_certificate(PGconn *conn)
-{
-   char       *peer_cn;
-   int         r;
-   int         len;
-   bool        result;
-
-   /*
-    * If told not to verify the peer name, don't do it. Return true
-    * indicating that the verification was successful.
-    */
-   if (strcmp(conn->sslmode, "verify-full") != 0)
-       return true;
-
-   /*
-    * Extract the common name from the certificate.
-    *
-    * XXX: Should support alternate names here
-    */
-   /* First find out the name's length and allocate a buffer for it. */
-   len = X509_NAME_get_text_by_NID(X509_get_subject_name(conn->peer),
-                                   NID_commonName, NULL, 0);
-   if (len == -1)
-   {
-       printfPQExpBuffer(&conn->errorMessage,
-                         libpq_gettext("could not get server common name from server certificate\n"));
-       return false;
-   }
-   peer_cn = malloc(len + 1);
-   if (peer_cn == NULL)
-   {
-       printfPQExpBuffer(&conn->errorMessage,
-                         libpq_gettext("out of memory\n"));
-       return false;
-   }
-
-   r = X509_NAME_get_text_by_NID(X509_get_subject_name(conn->peer),
-                                 NID_commonName, peer_cn, len + 1);
-   if (r != len)
-   {
-       /* Got different length than on the first call. Shouldn't happen. */
-       printfPQExpBuffer(&conn->errorMessage,
-                         libpq_gettext("could not get server common name from server certificate\n"));
-       free(peer_cn);
-       return false;
-   }
-   peer_cn[len] = '\0';
-
-   /*
-    * Reject embedded NULLs in certificate common name to prevent attacks
-    * like CVE-2009-4034.
-    */
-   if (len != strlen(peer_cn))
-   {
-       printfPQExpBuffer(&conn->errorMessage,
-                         libpq_gettext("SSL certificate's common name contains embedded null\n"));
-       free(peer_cn);
-       return false;
-   }
-
-   /*
-    * We got the peer's common name. Now compare it against the originally
-    * given hostname.
-    */
-   if (!(conn->pghost && conn->pghost[0] != '\0'))
-   {
-       printfPQExpBuffer(&conn->errorMessage,
-                         libpq_gettext("host name must be specified for a verified SSL connection\n"));
-       result = false;
-   }
-   else
-   {
-       if (pg_strcasecmp(peer_cn, conn->pghost) == 0)
-           /* Exact name match */
-           result = true;
-       else if (wildcard_certificate_match(peer_cn, conn->pghost))
-           /* Matched wildcard certificate */
-           result = true;
-       else
-       {
-           printfPQExpBuffer(&conn->errorMessage,
-                             libpq_gettext("server common name \"%s\" does not match host name \"%s\"\n"),
-                             peer_cn, conn->pghost);
-           result = false;
-       }
-   }
-
-   free(peer_cn);
-   return result;
-}
-
-#ifdef ENABLE_THREAD_SAFETY
-/*
- * Callback functions for OpenSSL internal locking
- */
-
-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();
-}
-
-static pthread_mutex_t *pq_lockarray;
-
-static void
-pq_lockingcallback(int mode, int n, const char *file, int line)
-{
-   if (mode & CRYPTO_LOCK)
-   {
-       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");
-   }
-}
-#endif   /* ENABLE_THREAD_SAFETY */
-
-/*
- * Initialize SSL system, in particular creating the SSL_context object
- * that will be shared by all SSL-using connections in this process.
- *
- * In threadsafe mode, this includes setting up libcrypto callback functions
- * to do thread locking.
- *
- * If the caller has told us (through PQinitOpenSSL) that he's taking care
- * of libcrypto, 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 here.
- *
- * Returns 0 if OK, -1 on failure (with a message in conn->errorMessage).
- */
-static int
-init_ssl_system(PGconn *conn)
-{
-#ifdef ENABLE_THREAD_SAFETY
-#ifdef WIN32
-   /* Also see similar code in fe-connect.c, default_threadlock() */
-   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_init_crypto_lib)
-   {
-       /*
-        * If necessary, set up an array to hold locks for libcrypto.
-        * libcrypto will tell us how big to make this array.
-        */
-       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 libcrypto applications */
-           CRYPTO_set_id_callback(pq_threadidcallback);
-           CRYPTO_set_locking_callback(pq_lockingcallback);
-       }
-   }
-#endif   /* ENABLE_THREAD_SAFETY */
-
-   if (!SSL_context)
-   {
-       if (pq_init_ssl_lib)
-       {
-#if SSLEAY_VERSION_NUMBER >= 0x00907000L
-           OPENSSL_config(NULL);
-#endif
-           SSL_library_init();
-           SSL_load_error_strings();
-       }
-
-       /*
-        * We use SSLv23_method() because it can negotiate use of the highest
-        * mutually supported protocol version, while alternatives like
-        * TLSv1_2_method() permit only one specific version.  Note that we
-        * don't actually allow SSL v2 or v3, only TLS protocols (see below).
-        */
-       SSL_context = SSL_CTX_new(SSLv23_method());
-       if (!SSL_context)
-       {
-           char       *err = SSLerrmessage();
-
-           printfPQExpBuffer(&conn->errorMessage,
-                        libpq_gettext("could not create SSL context: %s\n"),
-                             err);
-           SSLerrfree(err);
-#ifdef ENABLE_THREAD_SAFETY
-           pthread_mutex_unlock(&ssl_config_mutex);
-#endif
-           return -1;
-       }
-
-       /* Disable old protocol versions */
-       SSL_CTX_set_options(SSL_context, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
-
-       /*
-        * Disable OpenSSL's moving-write-buffer sanity check, because it
-        * causes unnecessary failures in nonblocking send cases.
-        */
-       SSL_CTX_set_mode(SSL_context, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
-   }
-
-#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
- * 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_init_crypto_lib && ssl_open_connections > 0)
-       --ssl_open_connections;
-
-   if (pq_init_crypto_lib && ssl_open_connections == 0)
-   {
-       /* No connections left, unregister libcrypto callbacks */
-       CRYPTO_set_locking_callback(NULL);
-       CRYPTO_set_id_callback(NULL);
-
-       /*
-        * 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.
-        */
-   }
-
-   pthread_mutex_unlock(&ssl_config_mutex);
-#endif
-}
-
-/*
- * Initialize (potentially) per-connection SSL data, namely the
- * client certificate, private key, and trusted CA certs.
- *
- * conn->ssl must already be created.  It receives the connection's client
- * certificate and private key.  Note however that certificates also get
- * loaded into the SSL_context object, and are therefore accessible to all
- * connections in this process.  This should be OK as long as there aren't
- * any hash collisions among the certs.
- *
- * Returns 0 if OK, -1 on failure (with a message in conn->errorMessage).
- */
-static int
-initialize_SSL(PGconn *conn)
-{
-   struct stat buf;
-   char        homedir[MAXPGPATH];
-   char        fnbuf[MAXPGPATH];
-   char        sebuf[256];
-   bool        have_homedir;
-   bool        have_cert;
-   EVP_PKEY   *pkey = NULL;
-
-   /*
-    * We'll need the home directory if any of the relevant parameters are
-    * defaulted.  If pqGetHomeDirectory fails, act as though none of the
-    * files could be found.
-    */
-   if (!(conn->sslcert && strlen(conn->sslcert) > 0) ||
-       !(conn->sslkey && strlen(conn->sslkey) > 0) ||
-       !(conn->sslrootcert && strlen(conn->sslrootcert) > 0) ||
-       !(conn->sslcrl && strlen(conn->sslcrl) > 0))
-       have_homedir = pqGetHomeDirectory(homedir, sizeof(homedir));
-   else    /* won't need it */
-       have_homedir = false;
-
-   /* Read the client certificate file */
-   if (conn->sslcert && strlen(conn->sslcert) > 0)
-       strncpy(fnbuf, conn->sslcert, sizeof(fnbuf));
-   else if (have_homedir)
-       snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_CERT_FILE);
-   else
-       fnbuf[0] = '\0';
-
-   if (fnbuf[0] == '\0')
-   {
-       /* no home directory, proceed without a client cert */
-       have_cert = false;
-   }
-   else if (stat(fnbuf, &buf) != 0)
-   {
-       /*
-        * If file is not present, just go on without a client cert; server
-        * might or might not accept the connection.  Any other error,
-        * however, is grounds for complaint.
-        */
-       if (errno != ENOENT && errno != ENOTDIR)
-       {
-           printfPQExpBuffer(&conn->errorMessage,
-              libpq_gettext("could not open certificate file \"%s\": %s\n"),
-                             fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
-           return -1;
-       }
-       have_cert = false;
-   }
-   else
-   {
-       /*
-        * Cert file exists, so load it.  Since OpenSSL doesn't provide the
-        * equivalent of "SSL_use_certificate_chain_file", we actually have to
-        * load the file twice.  The first call loads any extra certs after
-        * the first one into chain-cert storage associated with the
-        * SSL_context.  The second call loads the first cert (only) into the
-        * SSL object, where it will be correctly paired with the private key
-        * we load below.  We do it this way so that each connection
-        * 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
-       int         rc;
-
-       if ((rc = pthread_mutex_lock(&ssl_config_mutex)))
-       {
-           printfPQExpBuffer(&conn->errorMessage,
-              libpq_gettext("could not acquire mutex: %s\n"), strerror(rc));
-           return -1;
-       }
-#endif
-       if (SSL_CTX_use_certificate_chain_file(SSL_context, fnbuf) != 1)
-       {
-           char       *err = SSLerrmessage();
-
-           printfPQExpBuffer(&conn->errorMessage,
-              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();
-
-           printfPQExpBuffer(&conn->errorMessage,
-              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
-   }
-
-   /*
-    * Read the SSL key. If a key is specified, treat it as an engine:key
-    * combination if there is colon present - we don't support files with
-    * colon in the name. The exception is if the second character is a colon,
-    * in which case it can be a Windows filename with drive specification.
-    */
-   if (have_cert && conn->sslkey && strlen(conn->sslkey) > 0)
-   {
-#ifdef USE_SSL_ENGINE
-       if (strchr(conn->sslkey, ':')
-#ifdef WIN32
-           && conn->sslkey[1] != ':'
-#endif
-           )
-       {
-           /* Colon, but not in second character, treat as engine:key */
-           char       *engine_str = strdup(conn->sslkey);
-           char       *engine_colon;
-
-           if (engine_str == NULL)
-           {
-               printfPQExpBuffer(&conn->errorMessage,
-                                 libpq_gettext("out of memory\n"));
-               return -1;
-           }
-
-           /* cannot return NULL because we already checked before strdup */
-           engine_colon = strchr(engine_str, ':');
-
-           *engine_colon = '\0';       /* engine_str now has engine name */
-           engine_colon++;     /* engine_colon now has key name */
-
-           conn->engine = ENGINE_by_id(engine_str);
-           if (conn->engine == NULL)
-           {
-               char       *err = SSLerrmessage();
-
-               printfPQExpBuffer(&conn->errorMessage,
-                    libpq_gettext("could not load SSL engine \"%s\": %s\n"),
-                                 engine_str, err);
-               SSLerrfree(err);
-               free(engine_str);
-               return -1;
-           }
-
-           if (ENGINE_init(conn->engine) == 0)
-           {
-               char       *err = SSLerrmessage();
-
-               printfPQExpBuffer(&conn->errorMessage,
-               libpq_gettext("could not initialize SSL engine \"%s\": %s\n"),
-                                 engine_str, err);
-               SSLerrfree(err);
-               ENGINE_free(conn->engine);
-               conn->engine = NULL;
-               free(engine_str);
-               return -1;
-           }
-
-           pkey = ENGINE_load_private_key(conn->engine, engine_colon,
-                                          NULL, NULL);
-           if (pkey == NULL)
-           {
-               char       *err = SSLerrmessage();
-
-               printfPQExpBuffer(&conn->errorMessage,
-                                 libpq_gettext("could not read private SSL key \"%s\" from engine \"%s\": %s\n"),
-                                 engine_colon, engine_str, err);
-               SSLerrfree(err);
-               ENGINE_finish(conn->engine);
-               ENGINE_free(conn->engine);
-               conn->engine = NULL;
-               free(engine_str);
-               return -1;
-           }
-           if (SSL_use_PrivateKey(conn->ssl, pkey) != 1)
-           {
-               char       *err = SSLerrmessage();
-
-               printfPQExpBuffer(&conn->errorMessage,
-                                 libpq_gettext("could not load private SSL key \"%s\" from engine \"%s\": %s\n"),
-                                 engine_colon, engine_str, err);
-               SSLerrfree(err);
-               ENGINE_finish(conn->engine);
-               ENGINE_free(conn->engine);
-               conn->engine = NULL;
-               free(engine_str);
-               return -1;
-           }
-
-           free(engine_str);
-
-           fnbuf[0] = '\0';    /* indicate we're not going to load from a
-                                * file */
-       }
-       else
-#endif   /* USE_SSL_ENGINE */
-       {
-           /* PGSSLKEY is not an engine, treat it as a filename */
-           strncpy(fnbuf, conn->sslkey, sizeof(fnbuf));
-       }
-   }
-   else if (have_homedir)
-   {
-       /* No PGSSLKEY specified, load default file */
-       snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_KEY_FILE);
-   }
-   else
-       fnbuf[0] = '\0';
-
-   if (have_cert && fnbuf[0] != '\0')
-   {
-       /* read the client key from file */
-
-       if (stat(fnbuf, &buf) != 0)
-       {
-           printfPQExpBuffer(&conn->errorMessage,
-                             libpq_gettext("certificate present, but not private key file \"%s\"\n"),
-                             fnbuf);
-           return -1;
-       }
-#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);
-           return -1;
-       }
-#endif
-
-       if (SSL_use_PrivateKey_file(conn->ssl, fnbuf, SSL_FILETYPE_PEM) != 1)
-       {
-           char       *err = SSLerrmessage();
-
-           printfPQExpBuffer(&conn->errorMessage,
-              libpq_gettext("could not load private key file \"%s\": %s\n"),
-                             fnbuf, err);
-           SSLerrfree(err);
-           return -1;
-       }
-   }
-
-   /* verify that the cert and key go together */
-   if (have_cert &&
-       SSL_check_private_key(conn->ssl) != 1)
-   {
-       char       *err = SSLerrmessage();
-
-       printfPQExpBuffer(&conn->errorMessage,
-                         libpq_gettext("certificate does not match private key file \"%s\": %s\n"),
-                         fnbuf, err);
-       SSLerrfree(err);
-       return -1;
-   }
-
-   /*
-    * If the root cert file exists, load it so we can perform certificate
-    * verification. If sslmode is "verify-full" we will also do further
-    * verification after the connection has been completed.
-    */
-   if (conn->sslrootcert && strlen(conn->sslrootcert) > 0)
-       strncpy(fnbuf, conn->sslrootcert, sizeof(fnbuf));
-   else if (have_homedir)
-       snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CERT_FILE);
-   else
-       fnbuf[0] = '\0';
-
-   if (fnbuf[0] != '\0' &&
-       stat(fnbuf, &buf) == 0)
-   {
-       X509_STORE *cvstore;
-
-#ifdef ENABLE_THREAD_SAFETY
-       int         rc;
-
-       if ((rc = pthread_mutex_lock(&ssl_config_mutex)))
-       {
-           printfPQExpBuffer(&conn->errorMessage,
-              libpq_gettext("could not acquire mutex: %s\n"), strerror(rc));
-           return -1;
-       }
-#endif
-       if (SSL_CTX_load_verify_locations(SSL_context, fnbuf, NULL) != 1)
-       {
-           char       *err = SSLerrmessage();
-
-           printfPQExpBuffer(&conn->errorMessage,
-                             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;
-       }
-
-       if ((cvstore = SSL_CTX_get_cert_store(SSL_context)) != NULL)
-       {
-           if (conn->sslcrl && strlen(conn->sslcrl) > 0)
-               strncpy(fnbuf, conn->sslcrl, sizeof(fnbuf));
-           else if (have_homedir)
-               snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CRL_FILE);
-           else
-               fnbuf[0] = '\0';
-
-           /* Set the flags to check against the complete CRL chain */
-           if (fnbuf[0] != '\0' &&
-               X509_STORE_load_locations(cvstore, fnbuf, NULL) == 1)
-           {
-               /* 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);
-#else
-               char       *err = SSLerrmessage();
-
-               printfPQExpBuffer(&conn->errorMessage,
-                                 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);
-   }
-   else
-   {
-       /*
-        * stat() failed; assume root file doesn't exist.  If sslmode is
-        * verify-ca or verify-full, this is an error.  Otherwise, continue
-        * without performing any server cert verification.
-        */
-       if (conn->sslmode[0] == 'v')    /* "verify-ca" or "verify-full" */
-       {
-           /*
-            * The only way to reach here with an empty filename is if
-            * pqGetHomeDirectory failed.  That's a sufficiently unusual case
-            * that it seems worth having a specialized error message for it.
-            */
-           if (fnbuf[0] == '\0')
-               printfPQExpBuffer(&conn->errorMessage,
-                                 libpq_gettext("could not get home directory to locate root certificate file\n"
-                                               "Either provide the file or change sslmode to disable server certificate verification.\n"));
-           else
-               printfPQExpBuffer(&conn->errorMessage,
-               libpq_gettext("root certificate file \"%s\" does not exist\n"
-                             "Either provide the file or change sslmode to disable server certificate verification.\n"), fnbuf);
-           return -1;
-       }
-   }
-
-   /*
-    * If the OpenSSL version used supports it (from 1.0.0 on) and the user
-    * requested it, disable SSL compression.
-    */
-#ifdef SSL_OP_NO_COMPRESSION
-   if (conn->sslcompression && conn->sslcompression[0] == '0')
-   {
-       SSL_set_options(conn->ssl, SSL_OP_NO_COMPRESSION);
-   }
-#endif
-
-   return 0;
-}
-
-static void
-destroySSL(void)
-{
-   destroy_ssl_system();
-}
-
-/*
- * Attempt to negotiate SSL connection.
- */
-static PostgresPollingStatusType
-open_client_SSL(PGconn *conn)
-{
-   int         r;
-
-   r = SSL_connect(conn->ssl);
-   if (r <= 0)
-   {
-       int         err = SSL_get_error(conn->ssl, r);
-
-       switch (err)
-       {
-           case SSL_ERROR_WANT_READ:
-               return PGRES_POLLING_READING;
-
-           case SSL_ERROR_WANT_WRITE:
-               return PGRES_POLLING_WRITING;
-
-           case SSL_ERROR_SYSCALL:
-               {
-                   char        sebuf[256];
-
-                   if (r == -1)
-                       printfPQExpBuffer(&conn->errorMessage,
-                                   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"));
-                   close_SSL(conn);
-                   return PGRES_POLLING_FAILED;
-               }
-           case SSL_ERROR_SSL:
-               {
-                   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("unrecognized SSL error code: %d\n"),
-                                 err);
-               close_SSL(conn);
-               return PGRES_POLLING_FAILED;
-       }
-   }
-
-   /*
-    * We already checked the server certificate in initialize_SSL() using
-    * SSL_CTX_set_verify(), if root.crt exists.
-    */
-
-   /* get server certificate */
-   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"),
-                         err);
-       SSLerrfree(err);
-       close_SSL(conn);
-       return PGRES_POLLING_FAILED;
-   }
-
-   if (!verify_peer_name_matches_certificate(conn))
-   {
-       close_SSL(conn);
-       return PGRES_POLLING_FAILED;
-   }
-
-   /* SSL handshake is complete */
-   return PGRES_POLLING_OK;
-}
-
-/*
- * Close SSL connection.
- */
-static void
-close_SSL(PGconn *conn)
-{
-   bool        destroy_needed = false;
-
-   if (conn->ssl)
-   {
-       DECLARE_SIGPIPE_INFO(spinfo);
-
-       /*
-        * We can't destroy everything SSL-related here due to the possible
-        * later calls to OpenSSL routines which may need our thread
-        * callbacks, so set a flag here and check at the end.
-        */
-       destroy_needed = true;
-
-       DISABLE_SIGPIPE(conn, spinfo, (void) 0);
-       SSL_shutdown(conn->ssl);
-       SSL_free(conn->ssl);
-       conn->ssl = NULL;
-       /* We have to assume we got EPIPE */
-       REMEMBER_EPIPE(spinfo, true);
-       RESTORE_SIGPIPE(conn, spinfo);
-   }
-
-   if (conn->peer)
-   {
-       X509_free(conn->peer);
-       conn->peer = NULL;
-   }
-
-#ifdef USE_SSL_ENGINE
-   if (conn->engine)
-   {
-       ENGINE_finish(conn->engine);
-       ENGINE_free(conn->engine);
-       conn->engine = NULL;
-   }
-#endif
-
-   /*
-    * This will remove our SSL locking hooks, if this is the last SSL
-    * connection, which means we must wait to call it until after all SSL
-    * calls have been made, otherwise we can end up with a race condition and
-    * possible deadlocks.
-    *
-    * See comments above destroy_ssl_system().
-    */
-   if (destroy_needed)
-       pqsecure_destroy();
-}
-
-/*
- * Obtain reason string for last SSL error
- *
- * Some caution is needed here since ERR_reason_error_string will
- * return NULL if it doesn't recognize the error code.  We don't
- * want to return NULL ever.
- */
-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;
-   char       *errbuf;
-
-   errbuf = malloc(SSL_ERR_LEN);
-   if (!errbuf)
-       return ssl_nomem;
-   errcode = ERR_get_error();
-   if (errcode == 0)
-   {
-       snprintf(errbuf, SSL_ERR_LEN, libpq_gettext("no SSL error reported"));
-       return errbuf;
-   }
-   errreason = ERR_reason_error_string(errcode);
-   if (errreason != NULL)
-   {
-       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 OpenSSL object.
- */
-void *
-PQgetssl(PGconn *conn)
-{
-   if (!conn)
-       return NULL;
-   return conn->ssl;
-}
-#else                          /* !USE_SSL */
-
+#ifndef USE_SSL
 void *
 PQgetssl(PGconn *conn)
 {
index 4aeb4fad9875e605c4efd97f440193f35429a37c..60329048f23928f1d1ed19d209c694f4fdb5cb71 100644 (file)
@@ -73,14 +73,14 @@ typedef struct
 #endif
 #endif   /* ENABLE_SSPI */
 
-#ifdef USE_SSL
+#ifdef USE_OPENSSL
 #include <openssl/ssl.h>
 #include <openssl/err.h>
 
 #if (SSLEAY_VERSION_NUMBER >= 0x00907000L) && !defined(OPENSSL_NO_ENGINE)
 #define USE_SSL_ENGINE
 #endif
-#endif   /* USE_SSL */
+#endif   /* USE_OPENSSL */
 
 /*
  * POSTGRES backend dependent Constants.
@@ -427,6 +427,8 @@ struct pg_conn
    bool        allow_ssl_try;  /* Allowed to try SSL negotiation */
    bool        wait_ssl_try;   /* Delay SSL negotiation until after
                                 * attempting normal connection */
+   bool        ssl_in_use;
+#ifdef USE_OPENSSL
    SSL        *ssl;            /* SSL status, if have SSL connection */
    X509       *peer;           /* X509 cert of server */
 #ifdef USE_SSL_ENGINE
@@ -435,6 +437,7 @@ struct pg_conn
    void       *engine;         /* dummy field to keep struct the same if
                                 * OpenSSL version changes */
 #endif
+#endif   /* USE_OPENSSL */
 #endif   /* USE_SSL */
 
 #ifdef ENABLE_GSS
@@ -482,6 +485,24 @@ struct pg_cancel
  */
 extern char *const pgresStatus[];
 
+
+#ifdef USE_SSL
+
+#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
+
+#endif   /* USE_SSL */
+
 /* ----------------
  * Internal functions of libpq
  * Functions declared here need to be visible across files of libpq,
@@ -603,6 +624,8 @@ extern PostgresPollingStatusType pqsecure_open_client(PGconn *);
 extern void pqsecure_close(PGconn *);
 extern ssize_t pqsecure_read(PGconn *, void *ptr, size_t len);
 extern ssize_t pqsecure_write(PGconn *, const void *ptr, size_t len);
+extern ssize_t pqsecure_raw_read(PGconn *, void *ptr, size_t len);
+extern ssize_t pqsecure_raw_write(PGconn *, const void *ptr, size_t len);
 
 #if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
 extern int pq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending);
@@ -610,6 +633,16 @@ extern void pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending,
                 bool got_epipe);
 #endif
 
+/*
+ * The SSL implementatation provides these functions (fe-secure-openssl.c)
+ */
+extern void pgtls_init_library(bool do_ssl, int do_crypto);
+extern int pgtls_init(PGconn *conn);
+extern PostgresPollingStatusType pgtls_open_client(PGconn *conn);
+extern void pgtls_close(PGconn *conn);
+extern ssize_t pgtls_read(PGconn *conn, void *ptr, size_t len);
+extern ssize_t pgtls_write(PGconn *conn, const void *ptr, size_t len);
+
 /*
  * this is so that we can check if a connection is non-blocking internally
  * without the overhead of a function call
index 99fef27375b48b9e77e35443693ac7652afb6114..39a0bc9e445f1a65e74703c2bc1edb56c18d4db0 100644 (file)
@@ -2,7 +2,7 @@
 
 # Will build a static library libpq(d).lib
 #        and a dynamic library libpq(d).dll with import library libpq(d)dll.lib
-# USE_SSL=1 will compile with OpenSSL
+# USE_OPENSSL=1 will compile with OpenSSL
 # USE_KFW=1 will compile with kfw(kerberos for Windows)
 # DEBUG=1 compiles with debugging symbols
 # ENABLE_THREAD_SAFETY=1 compiles with threading enabled
@@ -124,6 +124,9 @@ CLEAN :
    -@erase "$(OUTDIR)\$(OUTFILENAME).dll.manifest"
    -@erase "$(OUTDIR)\*.idb"
    -@erase pg_config_paths.h"
+!IFDEF USE_OPENSSL
+   -@erase "$(INTDIR)\fe-secure-openssl.obj"
+!ENDIF
 
 
 LIB32=link.exe -lib
@@ -164,6 +167,9 @@ LIB32_OBJS= \
    "$(INTDIR)\win32error.obj" \
    "$(INTDIR)\win32setlocale.obj" \
    "$(INTDIR)\pthread-win32.obj"
+!IFDEF USE_OPENSSL
+   LIB32_OBJS=$(LIB32_OBJS) "$(INTDIR)\fe-secure-openssl.obj"
+!ENDIF
 
 
 config: ..\..\include\pg_config.h ..\..\include\pg_config_ext.h pg_config_paths.h  ..\..\include\pg_config_os.h
@@ -189,8 +195,8 @@ CPP_PROJ=/nologo /W3 /EHsc $(OPT) /I "..\..\include" /I "..\..\include\port\win3
  /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c  \
  /D "_CRT_SECURE_NO_DEPRECATE" $(ADD_DEFINES)
 
-!IFDEF USE_SSL
-CPP_PROJ=$(CPP_PROJ) /D USE_SSL
+!IFDEF USE_OPENSSL
+CPP_PROJ=$(CPP_PROJ) /D USE_OPENSSL
 SSL_LIBS=ssleay32.lib libeay32.lib gdi32.lib
 !ENDIF
 
index b71da67f5bef92281a99a164b5829c65fe33830d..e6fb3ecdecf8e8ffc0d07df4fb70b845fb97f089 100644 (file)
@@ -117,6 +117,12 @@ sub mkvcbuild
    $postgres->AddLibrary('ws2_32.lib');
    $postgres->AddLibrary('wldap32.lib') if ($solution->{options}->{ldap});
    $postgres->FullExportDLL('postgres.lib');
+   # The OBJS scraper doesn't know about ifdefs, so remove be-secure-openssl.c
+   # if building without OpenSSL
+   if (!$solution->{options}->{openssl})
+   {
+       $postgres->RemoveFile('src\backend\libpq\be-secure-openssl.c');
+   }
 
    my $snowball = $solution->AddProject('dict_snowball', 'dll', '',
        'src\backend\snowball');
@@ -276,6 +282,12 @@ sub mkvcbuild
    $libpq->ReplaceFile('src\interfaces\libpq\libpqrc.c',
        'src\interfaces\libpq\libpq.rc');
    $libpq->AddReference($libpgport);
+   # The OBJS scraper doesn't know about ifdefs, so remove fe-secure-openssl.c
+   # if building without OpenSSL
+   if (!$solution->{options}->{openssl})
+   {
+       $libpq->RemoveFile('src\interfaces\libpq\fe-secure-openssl.c');
+   }
 
    my $libpqwalreceiver =
      $solution->AddProject('libpqwalreceiver', 'dll', '',
index e49c3f4275f025769d0d1139b0463a7c1c5d7e66..39e41f673830b921e263e00aba85aca77347162a 100644 (file)
@@ -182,7 +182,7 @@ sub GenerateFiles
          if ($self->{options}->{integer_datetimes});
        print O "#define USE_LDAP 1\n"   if ($self->{options}->{ldap});
        print O "#define HAVE_LIBZ 1\n"  if ($self->{options}->{zlib});
-       print O "#define USE_SSL 1\n"    if ($self->{options}->{openssl});
+       print O "#define USE_OPENSSL 1\n" if ($self->{options}->{openssl});
        print O "#define ENABLE_NLS 1\n" if ($self->{options}->{nls});
 
        print O "#define BLCKSZ ", 1024 * $self->{options}->{blocksize}, "\n";
@@ -628,7 +628,7 @@ sub GetFakeConfigure
    $cfg .= ' --with-ldap'  if ($self->{options}->{ldap});
    $cfg .= ' --without-zlib' unless ($self->{options}->{zlib});
    $cfg .= ' --with-extra-version' if ($self->{options}->{extraver});
-   $cfg .= ' --with-openssl'       if ($self->{options}->{ssl});
+   $cfg .= ' --with-openssl'       if ($self->{options}->{openssl});
    $cfg .= ' --with-ossp-uuid'     if ($self->{options}->{uuid});
    $cfg .= ' --with-libxml'        if ($self->{options}->{xml});
    $cfg .= ' --with-libxslt'       if ($self->{options}->{xslt});
index 20aee8b702a6f9852ad6ace894d2d203f8803cf5..e4d48105c25f08e2397085fccad046c3e31301f9 100644 (file)
@@ -16,7 +16,7 @@ our $config = {
    tcl      => undef,   # --with-tls=<path>
    perl     => undef,   # --with-perl
    python   => undef,   # --with-python=<path>
-   openssl  => undef,   # --with-ssl=<path>
+   openssl  => undef,   # --with-openssl=<path>
    uuid     => undef,   # --with-ossp-uuid
    xml      => undef,   # --with-libxml=<path>
    xslt     => undef,   # --with-libxslt=<path>