Reorganize functions in be-secure-openssl.c
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>
Mon, 18 Aug 2014 10:04:47 +0000 (13:04 +0300)
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>
Mon, 18 Aug 2014 10:12:40 +0000 (13:12 +0300)
Move the functions within the file so that public interface functions come
first, followed by internal functions. Previously, be_tls_write was first,
then internal stuff, and finally the rest of the public interface, which
clearly didn't make much sense.

Per Andres Freund's complaint.

src/backend/libpq/be-secure-openssl.c

index f7bdcb7f10ef67606c5e7caf78a369ea5daaa2f6..8d8f12952a4a4f14a15f8647b96935e13d68fb39 100644 (file)
 #include "utils/memutils.h"
 
 
+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(Port *port, int fd);
 
 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_ecdh(void);
 static const char *SSLerrmessage(void);
 
 /* are we in the middle of a renegotiation? */
@@ -153,870 +158,876 @@ AaqLulO7R8Ifa1SwF2DteSGVtgWEN8gDpN3RBmmPTDngyF2DHb5qmpnznwtFKdTL\n\
 KWbuHn491xNO25CQWMtem80uKw+pTnisBRF/454n1Jnhub144YRBoN8CAQI=\n\
 -----END DH PARAMETERS-----\n";
 
+
+/* ------------------------------------------------------------ */
+/*                      Public interface                       */
+/* ------------------------------------------------------------ */
+
 /*
- * Write data to a secure connection.
+ * Initialize global SSL context.
  */
-ssize_t
-be_tls_write(Port *port, void *ptr, size_t len)
+void
+be_tls_init(void)
 {
-   ssize_t     n;
-   int         err;
+   struct stat buf;
 
-   /*
-    * 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)
+   STACK_OF(X509_NAME) *root_cert_list = NULL;
+
+   if (!SSL_context)
    {
-       in_ssl_renegotiation = true;
+#if SSLEAY_VERSION_NUMBER >= 0x0907000L
+       OPENSSL_config(NULL);
+#endif
+       SSL_library_init();
+       SSL_load_error_strings();
 
        /*
-        * 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.
+        * 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_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;
+       SSL_context = SSL_CTX_new(SSLv23_method());
+       if (!SSL_context)
+           ereport(FATAL,
+                   (errmsg("could not create SSL context: %s",
+                           SSLerrmessage())));
 
-           /*
-            * 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")));
-           }
-       }
-   }
+       /*
+        * 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);
 
-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;
-   }
+       /*
+        * 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 (n >= 0)
-   {
-       /* is renegotiation complete? */
-       if (in_ssl_renegotiation &&
-           SSL_num_renegotiations(port->ssl) >= 1)
-       {
-           in_ssl_renegotiation = false;
-           port->count = 0;
-       }
+       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)));
 
        /*
-        * 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.
+        * 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 (in_ssl_renegotiation &&
-           port->count > ssl_renegotiation_limit * 1024L)
+#if !defined(WIN32) && !defined(__CYGWIN__)
+       if (!S_ISREG(buf.st_mode) || buf.st_mode & (S_IRWXG | S_IRWXO))
            ereport(FATAL,
-                   (errcode(ERRCODE_PROTOCOL_VIOLATION),
-                    errmsg("SSL failed to renegotiate connection before limit expired")));
+                   (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())));
    }
 
-   return n;
-}
+   /* 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);
 
-/* ------------------------------------------------------------ */
-/*                     OpenSSL specific code                   */
-/* ------------------------------------------------------------ */
+   /* set up ephemeral ECDH keys */
+   initialize_ecdh();
 
-/*
- * 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?
- */
+   /* 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)");
 
-static bool my_bio_initialized = false;
-static BIO_METHOD my_bio_methods;
+   /* Let server choose order */
+   if (SSLPreferServerCiphers)
+       SSL_CTX_set_options(SSL_context, SSL_OP_CIPHER_SERVER_PREFERENCE);
 
-static int
-my_sock_read(BIO *h, char *buf, int size)
-{
-   int         res = 0;
+   /*
+    * 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())));
+   }
 
-   if (buf != NULL)
+   /*----------
+    * Load the Certificate Revocation List (CRL).
+    * http://searchsecurity.techtarget.com/sDefinition/0,,sid14_gci803160,00.html
+    *----------
+    */
+   if (ssl_crl_file[0])
    {
-       res = secure_raw_read(((Port *)h->ptr), buf, size);
-       BIO_clear_retry_flags(h);
-       if (res <= 0)
+       X509_STORE *cvstore = SSL_CTX_get_cert_store(SSL_context);
+
+       if (cvstore)
        {
-           /* If we were interrupted, tell caller to retry */
-           if (errno == EINTR)
+           /* Set the flags to check against the complete CRL chain */
+           if (X509_STORE_load_locations(cvstore, ssl_crl_file, NULL) == 1)
            {
-               BIO_set_retry_read(h);
+               /* 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())));
+       }
+   }
 
-   return res;
+   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);
+   }
 }
 
-static int
-my_sock_write(BIO *h, const char *buf, int size)
+/*
+ * Attempt to negotiate SSL connection.
+ */
+int
+be_tls_open_server(Port *port)
 {
-   int         res = 0;
+   int         r;
+   int         err;
 
-   res = secure_raw_write(((Port *) h->ptr), buf, size);
-   BIO_clear_retry_flags(h);
-   if (res <= 0)
+   Assert(!port->ssl);
+   Assert(!port->peer);
+
+   if (!(port->ssl = SSL_new(SSL_context)))
    {
-       if (errno == EINTR)
+       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)
        {
-           BIO_set_retry_write(h);
+           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;
    }
 
-   return res;
-}
+   port->count = 0;
 
-static BIO_METHOD *
-my_BIO_s_socket(void)
-{
-   if (!my_bio_initialized)
+   /* 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)
    {
-       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;
-}
+       int         len;
 
-/* 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;
+       len = X509_NAME_get_text_by_NID(X509_get_subject_name(port->peer),
+                                       NID_commonName, NULL, 0);
+       if (len != -1)
+       {
+           char       *peer_cn;
 
-   bio = BIO_new(my_BIO_s_socket());
+           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;
+           }
 
-   if (bio == NULL)
-   {
-       SSLerr(SSL_F_SSL_SET_FD, ERR_R_BUF_LIB);
-       goto err;
+           /*
+            * 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;
    }
-   /* 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;
+   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;
 }
 
 /*
- * 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.
+ * Close SSL connection.
  */
-static DH  *
-load_dh_file(int keylength)
+void
+be_tls_close(Port *port)
 {
-   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)
+   if (port->ssl)
    {
-       elog(LOG, "DH errors (%s): %d bits expected, %d bits found",
-            fnbuf, keylength, 8 * DH_size(dh));
-       dh = NULL;
+       SSL_shutdown(port->ssl);
+       SSL_free(port->ssl);
+       port->ssl = NULL;
+       port->ssl_in_use = false;
    }
 
-   /* make sure the DH parameters are usable */
-   if (dh != NULL)
+   if (port->peer)
    {
-       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;
-       }
+       X509_free(port->peer);
+       port->peer = NULL;
    }
 
-   return dh;
+   if (port->peer_cn)
+   {
+       pfree(port->peer_cn);
+       port->peer_cn = NULL;
+   }
 }
 
 /*
- * 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.
+ * Read data from a secure connection.
  */
-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)
+ssize_t
+be_tls_read(Port *port, void *ptr, size_t len)
 {
-   DH         *r = NULL;
-   static DH  *dh = NULL;
-   static DH  *dh512 = NULL;
-   static DH  *dh1024 = NULL;
-   static DH  *dh2048 = NULL;
-   static DH  *dh4096 = NULL;
+   ssize_t     n;
+   int         err;
 
-   switch (keylength)
+rloop:
+   errno = 0;
+   n = SSL_read(port->ssl, ptr, len);
+   err = SSL_get_error(port->ssl, n);
+   switch (err)
    {
-       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;
+       case SSL_ERROR_NONE:
+           port->count += n;
            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;
+       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 4096:
-           if (dh4096 == NULL)
-               dh4096 = load_dh_file(keylength);
-           if (dh4096 == NULL)
-               dh4096 = load_dh_buffer(file_dh4096, sizeof file_dh4096);
-           r = dh4096;
+       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:
-           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)));
+           ereport(COMMERROR,
+                   (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                    errmsg("unrecognized SSL error code: %d",
+                           err)));
+           errno = ECONNRESET;
+           n = -1;
            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);
+   return n;
 }
-#else
-#define initialize_ecdh()
-#endif
 
 /*
- * Initialize global SSL context.
+ * Write data to a secure connection.
  */
-void
-be_tls_init(void)
+ssize_t
+be_tls_write(Port *port, void *ptr, size_t len)
 {
-   struct stat buf;
-
-   STACK_OF(X509_NAME) *root_cert_list = NULL;
+   ssize_t     n;
+   int         err;
 
-   if (!SSL_context)
+   /*
+    * 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)
    {
-#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)));
+       in_ssl_renegotiation = true;
 
        /*
-        * Require no public access to key file.
+        * 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.
         *
-        * 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)
+        * OpenSSL provides SSL_renegotiation_pending(), but this doesn't
+        * seem to work in testing.
         */
-#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)");
+       SSL_clear_num_renegotiations(port->ssl);
 
-   /* Let server choose order */
-   if (SSLPreferServerCiphers)
-       SSL_CTX_set_options(SSL_context, SSL_OP_CIPHER_SERVER_PREFERENCE);
+       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;
 
-   /*
-    * 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())));
+           /*
+            * 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")));
+           }
+       }
    }
 
-   /*----------
-    * Load the Certificate Revocation List (CRL).
-    * http://searchsecurity.techtarget.com/sDefinition/0,,sid14_gci803160,00.html
-    *----------
-    */
-   if (ssl_crl_file[0])
+wloop:
+   errno = 0;
+   n = SSL_write(port->ssl, ptr, len);
+   err = SSL_get_error(port->ssl, n);
+   switch (err)
    {
-       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.")));
+       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;
            }
-           else
-               ereport(FATAL,
-                       (errmsg("could not load SSL certificate revocation list file \"%s\": %s",
-                               ssl_crl_file, SSLerrmessage())));
-       }
+           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 (ssl_ca_file[0])
+   if (n >= 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;
+       /* is renegotiation complete? */
+       if (in_ssl_renegotiation &&
+           SSL_num_renegotiations(port->ssl) >= 1)
+       {
+           in_ssl_renegotiation = false;
+           port->count = 0;
+       }
 
        /*
-        * 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.
+        * 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.
         */
-       SSL_CTX_set_client_CA_list(SSL_context, root_cert_list);
+       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;
 }
 
+/* ------------------------------------------------------------ */
+/*                     Internal functions                      */
+/* ------------------------------------------------------------ */
+
 /*
- * Attempt to negotiate SSL connection.
+ * 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?
  */
-int
-be_tls_open_server(Port *port)
-{
-   int         r;
-   int         err;
 
-   Assert(!port->ssl);
-   Assert(!port->peer);
+static bool my_bio_initialized = false;
+static BIO_METHOD my_bio_methods;
 
-   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))
+static int
+my_sock_read(BIO *h, char *buf, int size)
+{
+   int         res = 0;
+
+   if (buf != NULL)
    {
-       ereport(COMMERROR,
-               (errcode(ERRCODE_PROTOCOL_VIOLATION),
-                errmsg("could not set SSL socket: %s",
-                       SSLerrmessage())));
-       be_tls_close(port);
-       return -1;
+       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);
+           }
+       }
    }
-   port->ssl_in_use = true;
 
-aloop:
-   r = SSL_accept(port->ssl);
-   if (r <= 0)
+   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)
    {
-       err = SSL_get_error(port->ssl, r);
-       switch (err)
+       if (errno == EINTR)
        {
-           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;
+           BIO_set_retry_write(h);
        }
-       be_tls_close(port);
-       return -1;
    }
 
-   port->count = 0;
+   return res;
+}
 
-   /* Get client certificate, if available. */
-   port->peer = SSL_get_peer_certificate(port->ssl);
+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;
+}
 
-   /* and extract the Common Name from it. */
-   port->peer_cn = NULL;
-   port->peer_cert_valid = false;
-   if (port->peer != NULL)
+/* 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)
    {
-       int         len;
+       SSLerr(SSL_F_SSL_SET_FD, ERR_R_BUF_LIB);
+       goto err;
+   }
+   /* Use 'ptr' to store pointer to PGconn */
+   bio->ptr = port;
 
-       len = X509_NAME_get_text_by_NID(X509_get_subject_name(port->peer),
-                                       NID_commonName, NULL, 0);
-       if (len != -1)
-       {
-           char       *peer_cn;
+   BIO_set_fd(bio, fd, BIO_NOCLOSE);
+   SSL_set_bio(port->ssl, bio, bio);
+   ret = 1;
+err:
+   return ret;
+}
 
-           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;
-           }
+/*
+ * 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;
 
-           /*
-            * 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;
-           }
+   /* 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;
+   }
 
-           port->peer_cn = peer_cn;
+   /* 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;
        }
-       port->peer_cert_valid = true;
    }
 
-   ereport(DEBUG2,
-           (errmsg("SSL connection from \"%s\"",
-                   port->peer_cn ? port->peer_cn : "(anonymous)")));
+   return dh;
+}
 
-   /* set up debugging/info callback */
-   SSL_CTX_set_info_callback(SSL_context, info_cb);
+/*
+ * Load hardcoded DH parameters.
+ *
+ * To prevent problems if the DH parameters files don't even
+ * exist, we can load DH parameters hardcoded into this file.
+ */
+static DH  *
+load_dh_buffer(const char *buffer, size_t len)
+{
+   BIO        *bio;
+   DH         *dh = NULL;
 
-   return 0;
+   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;
 }
 
 /*
- * Close SSL connection.
+ * 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.
  */
-void
-be_tls_close(Port *port)
+static DH  *
+tmp_dh_cb(SSL *s, int is_export, int keylength)
 {
-   if (port->ssl)
-   {
-       SSL_shutdown(port->ssl);
-       SSL_free(port->ssl);
-       port->ssl = NULL;
-       port->ssl_in_use = false;
-   }
+   DH         *r = NULL;
+   static DH  *dh = NULL;
+   static DH  *dh512 = NULL;
+   static DH  *dh1024 = NULL;
+   static DH  *dh2048 = NULL;
+   static DH  *dh4096 = NULL;
 
-   if (port->peer)
+   switch (keylength)
    {
-       X509_free(port->peer);
-       port->peer = NULL;
+       case 512:
+           if (dh512 == NULL)
+               dh512 = load_dh_file(keylength);
+           if (dh512 == NULL)
+               dh512 = load_dh_buffer(file_dh512, sizeof file_dh512);
+           r = dh512;
+           break;
+
+       case 1024:
+           if (dh1024 == NULL)
+               dh1024 = load_dh_file(keylength);
+           if (dh1024 == NULL)
+               dh1024 = load_dh_buffer(file_dh1024, sizeof file_dh1024);
+           r = dh1024;
+           break;
+
+       case 2048:
+           if (dh2048 == NULL)
+               dh2048 = load_dh_file(keylength);
+           if (dh2048 == NULL)
+               dh2048 = load_dh_buffer(file_dh2048, sizeof file_dh2048);
+           r = dh2048;
+           break;
+
+       case 4096:
+           if (dh4096 == NULL)
+               dh4096 = load_dh_file(keylength);
+           if (dh4096 == NULL)
+               dh4096 = load_dh_buffer(file_dh4096, sizeof file_dh4096);
+           r = dh4096;
+           break;
+
+       default:
+           if (dh == NULL)
+               dh = load_dh_file(keylength);
+           r = dh;
    }
 
-   if (port->peer_cn)
+   /* this may take a long time, but it may be necessary... */
+   if (r == NULL || 8 * DH_size(r) < keylength)
    {
-       pfree(port->peer_cn);
-       port->peer_cn = NULL;
+       ereport(DEBUG2,
+               (errmsg_internal("DH: generating parameters (%d bits)",
+                                keylength)));
+       r = DH_generate_parameters(keylength, DH_GENERATOR_2, NULL, NULL);
    }
+
+   return r;
 }
 
-ssize_t
-be_tls_read(Port *port, void *ptr, size_t len)
+/*
+ * 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)
 {
-   ssize_t     n;
-   int         err;
+   return ok;
+}
 
-rloop:
-   errno = 0;
-   n = SSL_read(port->ssl, ptr, len);
-   err = SSL_get_error(port->ssl, n);
-   switch (err)
+/*
+ * 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_ERROR_NONE:
-           port->count += n;
+       case SSL_CB_HANDSHAKE_START:
+           ereport(DEBUG4,
+                   (errmsg_internal("SSL: handshake start")));
            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;
-           }
+       case SSL_CB_HANDSHAKE_DONE:
+           ereport(DEBUG4,
+                   (errmsg_internal("SSL: handshake done")));
            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;
+       case SSL_CB_ACCEPT_LOOP:
+           ereport(DEBUG4,
+                   (errmsg_internal("SSL: accept loop")));
            break;
-       default:
-           ereport(COMMERROR,
-                   (errcode(ERRCODE_PROTOCOL_VIOLATION),
-                    errmsg("unrecognized SSL error code: %d",
-                           err)));
-           errno = ECONNRESET;
-           n = -1;
+       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;
+static void
+initialize_ecdh(void)
+{
+#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_ECDH)
+   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);
+#endif
 }
 
 /*