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
 }
 
 /*