]> granicus.if.org Git - postgresql/commitdiff
Break out OpenSSL-specific code to separate files.
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>
Mon, 11 Aug 2014 08:54:19 +0000 (11:54 +0300)
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>
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>