
--- postgresql-7.2.1.orig/src/include/libpq/libpq-be.h	Sun Nov 11 22:43:25 2001
+++ postgresql-7.2.1/src/include/libpq/libpq-be.h	Thu May 16 12:19:09 2002
@@ -70,6 +70,7 @@
 	 */
 #ifdef USE_SSL
 	SSL		   *ssl;
+	X509	   *peer;
 #endif
 } Port;
 
--- postgresql-7.2.1.orig/src/interfaces/libpq/fe-misc.c	Sun Dec  2 17:28:24 2001
+++ postgresql-7.2.1/src/interfaces/libpq/fe-misc.c	Thu May 16 17:01:20 2002
@@ -57,6 +57,9 @@
 #include "mb/pg_wchar.h"
 #endif
 
+#ifdef USE_SSL
+#include <openssl/e_os.h>
+#endif
 
 #define DONOTICE(conn,message) \
 	((*(conn)->noticeHook) ((conn)->noticeArg, (message)))
@@ -461,8 +464,30 @@
 tryAgain:
 #ifdef USE_SSL
 	if (conn->ssl)
+	{
 		nread = SSL_read(conn->ssl, conn->inBuffer + conn->inEnd,
 						 conn->inBufSize - conn->inEnd);
+		switch(SSL_get_error(conn->ssl, nread))
+		{
+		case SSL_ERROR_NONE:
+			break;
+		case SSL_ERROR_WANT_WRITE:
+		case SSL_ERROR_WANT_READ:
+		case SSL_ERROR_WANT_X509_LOOKUP:
+			/* blocked */
+			break;
+		case SSL_ERROR_SYSCALL:
+			SOCK_ERRNO = get_last_socket_error();
+			break;
+		case SSL_ERROR_SSL:
+		/*	ERR_print_errors() */
+			SOCK_ERRNO = ECONNRESET;
+			break;
+		case SSL_ERROR_ZERO_RETURN:
+			SOCK_ERRNO = ECONNRESET;
+			break;
+		}
+ 	}
 	else
 #endif
 		nread = recv(conn->sock, conn->inBuffer + conn->inEnd,
@@ -547,8 +572,30 @@
 tryAgain2:
 #ifdef USE_SSL
 	if (conn->ssl)
+	{
 		nread = SSL_read(conn->ssl, conn->inBuffer + conn->inEnd,
 						 conn->inBufSize - conn->inEnd);
+		switch(SSL_get_error(conn->ssl, nread))
+		{
+		case SSL_ERROR_NONE:
+			break;
+		case SSL_ERROR_WANT_WRITE:
+		case SSL_ERROR_WANT_READ:
+		case SSL_ERROR_WANT_X509_LOOKUP:
+			/* blocked */
+			break;
+		case SSL_ERROR_SYSCALL:
+			SOCK_ERRNO = get_last_socket_error();
+			break;
+		case SSL_ERROR_SSL:
+		/*	ERR_print_errors() */
+			SOCK_ERRNO = ECONNRESET;
+			break;
+		case SSL_ERROR_ZERO_RETURN:
+			SOCK_ERRNO = ECONNRESET;
+			break;
+		}
+	}
 	else
 #endif
 		nread = recv(conn->sock, conn->inBuffer + conn->inEnd,
@@ -638,7 +685,29 @@
 
 #ifdef USE_SSL
 		if (conn->ssl)
+		{
 			sent = SSL_write(conn->ssl, ptr, len);
+			switch(SSL_get_error(conn->ssl, sent))
+			{
+			case SSL_ERROR_NONE:
+				break;
+			case SSL_ERROR_WANT_WRITE:
+			case SSL_ERROR_WANT_READ:
+			case SSL_ERROR_WANT_X509_LOOKUP:
+				/* blocked */
+				break;
+			case SSL_ERROR_SYSCALL:
+				SOCK_ERRNO = get_last_socket_error();
+				break;
+			case SSL_ERROR_SSL:
+			/*	ERR_print_errors() */
+				SOCK_ERRNO = ECONNRESET;
+				break;
+			case SSL_ERROR_ZERO_RETURN:
+				SOCK_ERRNO = ECONNRESET;
+				break;
+			}
+		}
 		else
 #endif
 			sent = send(conn->sock, ptr, len, 0);
--- postgresql-7.2.1.orig/src/interfaces/libpq/fe-connect.c	Sat Nov 10 19:09:05 2001
+++ postgresql-7.2.1/src/interfaces/libpq/fe-connect.c	Thu May 16 20:53:31 2002
@@ -20,6 +20,11 @@
 #include <errno.h>
 #include <ctype.h>
 
+#ifdef USE_SSL
+#include <sys/stat.h>
+#include <sys/file.h>
+#endif
+
 #include "libpq-fe.h"
 #include "libpq-int.h"
 #include "fe-auth.h"
@@ -64,6 +69,8 @@
 
 #ifdef USE_SSL
 static SSL_CTX *SSL_context = NULL;
+static int clientCertCB(SSL *ssl, X509 **x509, EVP_PKEY **pkey);
+static int cliPasswordCB(char *buf, int size, int rwflag, void *userdata);
 #endif
 
 #define NOTIFYLIST_INITIAL_SIZE 10
@@ -136,6 +143,12 @@
 #ifdef USE_SSL
 	{"requiressl", "PGREQUIRESSL", "0", NULL,
 	"Require-SSL", "", 1},
+
+	{"clientcert", "PGCLIENTCERT", NULL, NULL,
+	"Client-Cert", "", 80},
+
+	{"clientkey", "PGCLIENTKEY", NULL, NULL,
+	"Client-Key", "", 80},
 #endif
 
 	/* Terminating entry --- MUST BE LAST */
@@ -310,6 +323,12 @@
 #ifdef USE_SSL
 	tmp = conninfo_getval(connOptions, "requiressl");
 	conn->require_ssl = tmp ? (tmp[0] == '1' ? true : false) : false;
+	tmp = conninfo_getval(connOptions, "clientcert");
+	conn->client_cert = tmp ? strdup(tmp) : NULL;
+	tmp = conninfo_getval(connOptions, "clientkey");
+	conn->client_key = tmp ? strdup(tmp) : NULL;
+	/* for now, require unencrypted client keys */
+	conn->clientKeyCB = NULL;  /* type is pem_passwd_cb */
 #endif
 
 	/*
@@ -506,6 +525,16 @@
 		conn->require_ssl = (tmp[0] == '1') ? true : false;
 	else
 		conn->require_ssl = 0;
+	if ((tmp = getenv("PGCLIENTCERT")) != NULL)
+		conn->client_cert = strdup(tmp);
+	else
+		conn->client_cert = NULL;
+	if ((tmp = getenv("PGCLIENTKEY")) != NULL)
+		conn->client_key = strdup(tmp);
+	else
+		conn->client_key = NULL;
+	/* for now, require unencrypted client keys */
+	conn->clientKeyCB = NULL;  /* type is pem_passwd_cb */
 #endif
 
 	if (error)
@@ -766,7 +795,6 @@
 						  conn->pgport);
 }
 
-
 /* ----------
  * connectDBStart -
  * Start to make a connection to the backend so it is ready to receive
@@ -967,8 +995,10 @@
 									  SSLerrmessage());
 					goto connect_errReturn;
 				}
+				SSL_CTX_set_client_cert_cb(SSL_context, clientCertCB);
 			}
 			if (!(conn->ssl = SSL_new(SSL_context)) ||
+				!SSL_set_app_data(conn->ssl, conn) ||
 				!SSL_set_fd(conn->ssl, conn->sock) ||
 				SSL_connect(conn->ssl) <= 0)
 			{
@@ -1901,7 +1931,12 @@
 	pqClearAsyncResult(conn);	/* deallocate result and curTuple */
 #ifdef USE_SSL
 	if (conn->ssl)
+		SSL_shutdown(conn->ssl);
 		SSL_free(conn->ssl);
+	if (conn->client_cert)
+		free(conn->client_cert);
+	if (conn->client_key)
+		free(conn->client_key);
 #endif
 	if (conn->sock >= 0)
 	{
@@ -2856,3 +2891,155 @@
 	/* Note: we expect the supplied string to end with a newline already. */
 	fprintf(stderr, "%s", message);
 }
+
+#if defined USE_SSL
+/* ----------
+ * load_pkey -
+ * For security reasons, we fail if the file is not a regular file,
+ * if it's group- or world-accessible, etc.  We should also verify
+ * the file's ownership.
+ * ----------
+ */
+static EVP_PKEY *load_pkey(const char *filename, pem_password_cb cb, void *u)
+{
+	int fd;
+	FILE *fp;
+	EVP_PKEY *pkey = NULL;
+	struct stat buf1, buf2;
+
+	if (lstat(filename, &buf1) == -1)
+		return NULL;
+	if (!S_ISREG(buf1.st_mode))
+		return NULL;
+	if ((buf1.st_mode & (S_IRWXG | S_IRWXO)) != 0)
+		return NULL;
+	if ((fd = open(filename, O_RDONLY)) == -1)
+		return NULL;
+	if (fstat(fd, &buf2) == -1)
+		return NULL;
+	if (buf1.st_dev != buf2.st_dev || buf1.st_ino != buf2.st_ino)
+		return NULL;
+
+	fp = fdopen(fd, "r");
+//	flock(fileno(fp), LOCK_SH);
+	pkey = PEM_read_PrivateKey(fp, NULL, cb, u);
+if (pkey == NULL) { printf("pkey = null\n"); }
+//	flock(fileno(fp), LOCK_UN);
+	fclose(fp);
+
+	return pkey;
+}
+
+/* ----------
+ * load_cert -
+ * Load user's certificate.
+ * ----------
+ */
+static X509 *load_cert(const char *filename)
+{
+	FILE *fp;
+	X509 *x = NULL;
+
+	if ((fp = fopen(filename, "r")) == NULL)
+		return NULL;
+
+	flock(fileno(fp), LOCK_SH);
+	x = PEM_read_X509(fp, NULL, NULL, NULL);
+	flock(fileno(fp), LOCK_UN);
+	fclose(fp);
+if (x == NULL) { ERR_print_errors_fp(stderr); }
+
+	return x;
+}
+
+/* ----------
+ * cliPasswordCB -
+ * Callback used to prompt for password from stdin.  The static buffer
+ * within getpass() is zeroed immediately after the data is copied.
+ * Returns -1 on error, otherwise the length of the password.
+ * This function isn't actually used yet.
+ * ----------
+ */
+static int 
+cliPasswordCB(char *buf, int size, int rwflag, void *userdata)
+{
+	char *p;
+	int klen;
+
+	p = getpass(gettext("Password for private key: "));
+	if (p == NULL)
+		return -1;
+
+	klen = strlen(p);
+	strncpy(buf, p, size);
+	memset(p, 0, 128);
+
+	if (klen > size)
+		klen = size;
+
+	return (klen < size) ? klen : size; 
+}
+
+/* ----------
+ * clientCertCB -
+ * Callback used by SSL to load client cert and key.  If conn->clientKeyCB
+ * is non-null, we can support encrypted private keys ad the like.
+ * returns 1 on success, 0 on no data, -1 on error.
+ * ----------
+ */
+static int 
+clientCertCB(SSL *ssl, X509 **x509, EVP_PKEY **pkey)
+{
+	PGconn *conn;
+/*	char cname[256]; */
+
+	/* this shouldn't be NULL, but if so quietly succeed. */
+	conn = SSL_get_app_data(ssl);
+	if (conn == NULL) 
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+			libpq_gettext("connecting with anonymous connection\n"));
+		return 0;
+	}
+
+	/* if neither cert nor key is present, return success. */
+	if (conn->client_cert == NULL && conn->client_key == NULL)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+			libpq_gettext("connecting with anonymous connection\n"));
+		return 0;
+	}
+
+	/* otherwise both cert and must be present. */
+	if (conn->client_cert == NULL || conn->client_key == NULL)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+			libpq_gettext("both cert and key must be specified\n"));
+		return -1;
+	}
+
+	if ((*pkey = load_pkey(conn->client_key, conn->clientKeyCB, conn)) == NULL)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+			libpq_gettext("load_pkey(%s) failed\n"), conn->client_key);
+		return -1;
+	}
+
+	if ((*x509 = load_cert(conn->client_cert)) == NULL)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+			libpq_gettext("load_cert(%s) failed\n"), conn->client_key);
+		EVP_PKEY_free(*pkey);
+		*pkey = NULL;
+		return -1;
+	}
+
+/*
+ *	X509_NAME_oneline(X509_get_subject_name(*x509), cname, sizeof cname);
+ *	printfPQExpBuffer(&conn->errorMessage,
+ *		libpq_gettext("connecting with client cert %s\n"), cname);
+ */
+
+	return 1;
+}
+#endif
--- postgresql-7.2.1.orig/src/backend/libpq/pqcomm.c	Tue Dec  4 13:57:22 2001
+++ postgresql-7.2.1/src/backend/libpq/pqcomm.c	Thu May 16 17:34:57 2002
@@ -80,6 +80,10 @@
 #include "libpq/libpq.h"
 #include "miscadmin.h"
 
+#ifdef USE_SSL
+#include <openssl/e_os.h>
+extern const char *SSLerrmessage(void);
+#endif
 
 static void pq_close(void);
 
@@ -491,8 +495,31 @@
 
 #ifdef USE_SSL
 		if (MyProcPort->ssl)
+		{
 			r = SSL_read(MyProcPort->ssl, PqRecvBuffer + PqRecvLength,
 						 PQ_BUFFER_SIZE - PqRecvLength);
+			switch (SSL_get_error(MyProcPort->ssl, r))
+			{
+			case SSL_ERROR_NONE:
+				break;
+			case SSL_ERROR_WANT_WRITE:
+			case SSL_ERROR_WANT_READ:
+			case SSL_ERROR_WANT_X509_LOOKUP:
+				/* blocked */
+				break;
+			case SSL_ERROR_SYSCALL:
+				errno = get_last_socket_error();
+				break;
+			case SSL_ERROR_SSL:
+				elog(ERROR, "SSL error: %s", SSLerrmessage());
+				errno = ECONNRESET;
+				break;
+			case SSL_ERROR_ZERO_RETURN:
+				elog(DEBUG, "SSL shutdown by peer");
+				errno = ECONNRESET;
+				break;
+			}
+		}
 		else
 #endif
 			r = recv(MyProcPort->sock, PqRecvBuffer + PqRecvLength,
@@ -666,7 +693,30 @@
 
 #ifdef USE_SSL
 		if (MyProcPort->ssl)
+		{
 			r = SSL_write(MyProcPort->ssl, bufptr, bufend - bufptr);
+			switch (SSL_get_error(MyProcPort->ssl, r))
+			{
+			case SSL_ERROR_NONE:
+				break;
+			case SSL_ERROR_WANT_WRITE:
+			case SSL_ERROR_WANT_READ:
+			case SSL_ERROR_WANT_X509_LOOKUP:
+				/* blocked */
+				break;
+			case SSL_ERROR_SYSCALL:
+				errno = get_last_socket_error();
+				break;
+			case SSL_ERROR_SSL:
+				elog(ERROR, "SSL error: %s", SSLerrmessage());
+				errno = ECONNRESET;
+				break;
+			case SSL_ERROR_ZERO_RETURN:
+				elog(DEBUG, "SSL shutdown by peer");
+				errno = ECONNRESET;
+				break;
+			}
+		}
 		else
 #endif
 			r = send(MyProcPort->sock, bufptr, bufend - bufptr, 0);
--- postgresql-7.2.1.orig/src/backend/postmaster/postmaster.c	Fri Mar 15 12:20:47 2002
+++ postgresql-7.2.1/src/backend/postmaster/postmaster.c	Thu May 16 23:04:04 2002
@@ -166,7 +166,21 @@
 #endif
 
 #ifdef USE_SSL
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+
 static SSL_CTX *SSL_context = NULL;		/* Global SSL context */
+static int postmaster_session_id_context = 1; /* anything will do */
+static int verifyCB (int ok, X509_STORE_CTX *ctx);
+static DH *load_dh_param(const char *filename);
+static EVP_PKEY *load_pkey(const char *filename);
+static X509 *load_cert(const char *filename);
+static DH *get_dh512(void);
+static DH *get_dh1024(void);
+static DH *get_dh2048(void);
+static DH *get_dh4096(void);
+static DH *tmp_dh_cb(SSL *, int, int);
 #endif
 
 /*
@@ -272,7 +286,7 @@
 
 #ifdef USE_SSL
 static void InitSSL(void);
-static const char *SSLerrmessage(void);
+const char *SSLerrmessage(void);
 #endif
 
 
@@ -1058,6 +1072,10 @@
 	enum CAC_state cac;
 	int32		len;
 	void	   *buf;
+#ifdef USE_SSL
+	const char *cipher = NULL;
+	char		buffer[256];
+#endif
 
 	if (pq_getbytes((char *) &len, 4) == EOF)
 	{
@@ -1124,6 +1142,29 @@
 					 SSLerrmessage());
 				return STATUS_ERROR;
 			}
+/*			if (context)
+ *				SSL_set_session_id_context(port->ssl, context,
+ *					strlen((char *) context);
+ */
+/*			SSL_clear(port->ssl); */
+/*			SSL_set_accept_state(port->ssl); */
+
+			port->peer = SSL_get_peer_certificate(port->ssl);
+			if (port->peer == NULL)
+				strncpy(buffer, "(anonymous)", sizeof buffer);
+			else
+			{
+				X509_NAME_oneline(X509_get_subject_name(port->peer),
+					buffer, sizeof buffer);
+			}
+			cipher = SSL_CIPHER_get_name(SSL_get_current_cipher(port->ssl));
+			elog(DEBUG, "SSL connection from %s with cipher %s", buffer,
+				cipher != NULL ? cipher : "(NONE)");
+			if (SSL_ctrl(port->ssl, SSL_CTRL_GET_FLAGS, 0, NULL) &
+				TLS1_FLAGS_TLS_PADDING_BUG)
+			{
+				elog(ERROR, "Peer has incorrect TLSv1 block padding");
+			}
 		}
 #endif
 		/* regular startup packet, cancel, etc packet should follow... */
@@ -1330,6 +1371,8 @@
 {
 #ifdef USE_SSL
 	if (conn->ssl)
+/*		this seems to shut down *all* connections, not just current one */
+/*		SSL_set_shutdown(conn->ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN); */
 		SSL_free(conn->ssl);
 #endif
 	free(conn);
@@ -2427,7 +2470,6 @@
 }
 
 #ifdef USE_SSL
-
 /*
  * Initialize SSL library and structures
  */
@@ -2435,9 +2477,32 @@
 InitSSL(void)
 {
 	char		fnbuf[2048];
+	int			verify_mode = SSL_VERIFY_PEER;
+//	char	   *inrand = NULL;  // entropy source
+	char	   *CAfile = NULL;
+	char	   *CApath = NULL;
+	X509	   *cert = NULL;
+	EVP_PKEY   *pkey = NULL;
 
 	SSL_load_error_strings();
 	SSL_library_init();
+
+	/*
+	 * set up an entropy source
+	 * app_RAND_load_X is in openssl/apps source.
+	 */
+#if 0
+	if (!app_RAND_load_file(NULL, NULL, 1) && inrand == NULL && !RAND_status())
+	{
+		postmaster_error("failed to load random: %s",
+						 SSLerrmessage());
+		ExitPostmaster(1);
+	}
+	if (inrand != NULL)
+		app_RAND_load_files(inrand);
+#endif
+
+/*	SSL_context = SSL_CTX_new(TLSv1_method()); */
 	SSL_context = SSL_CTX_new(SSLv23_method());
 	if (!SSL_context)
 	{
@@ -2445,26 +2510,64 @@
 						 SSLerrmessage());
 		ExitPostmaster(1);
 	}
+
+	/*
+	 * set up mechanism to verify certs
+	 */
+	if (CAfile || CApath)
+	{
+		if (!SSL_CTX_load_verify_locations(SSL_context, CAfile, CApath) ||
+			!SSL_CTX_set_default_verify_paths(SSL_context))
+		{
+			postmaster_error("failed to load verification paths: %s",
+							 SSLerrmessage());
+			ExitPostmaster(1);
+		}
+	}
+	if (CAfile != NULL)
+	{
+		SSL_CTX_set_client_CA_list(SSL_context,
+			SSL_load_client_CA_file(CAfile));
+	}
+
+	/*
+	 * set up server cert and private key
+	 */
 	snprintf(fnbuf, sizeof(fnbuf), "%s/server.crt", DataDir);
-	if (!SSL_CTX_use_certificate_file(SSL_context, fnbuf, SSL_FILETYPE_PEM))
+	if ((cert = load_cert(fnbuf)) == NULL ||
+		!SSL_CTX_use_certificate(SSL_context, cert))
 	{
 		postmaster_error("failed to load server certificate (%s): %s",
 						 fnbuf, SSLerrmessage());
 		ExitPostmaster(1);
 	}
 	snprintf(fnbuf, sizeof(fnbuf), "%s/server.key", DataDir);
-	if (!SSL_CTX_use_PrivateKey_file(SSL_context, fnbuf, SSL_FILETYPE_PEM))
+	if ((pkey = load_pkey(fnbuf)) == NULL ||
+		!SSL_CTX_use_PrivateKey(SSL_context, pkey))
 	{
 		postmaster_error("failed to load private key file (%s): %s",
 						 fnbuf, SSLerrmessage());
 		ExitPostmaster(1);
 	}
+	pkey = NULL;
 	if (!SSL_CTX_check_private_key(SSL_context))
 	{
 		postmaster_error("check of private key failed: %s",
 						 SSLerrmessage());
 		ExitPostmaster(1);
 	}
+
+	/*
+	 * make sure we use empheral DH keys
+	 */
+	SSL_CTX_set_tmp_dh_callback(SSL_context, tmp_dh_cb);
+	SSL_CTX_set_options(SSL_context, SSL_OP_SINGLE_DH_USE);
+
+	SSL_CTX_set_verify(SSL_context, verify_mode, verifyCB);
+
+	SSL_CTX_set_session_id_context(SSL_context,
+		(void*)&postmaster_session_id_context,
+		sizeof postmaster_session_id_context);
 }
 
 /*
@@ -2474,7 +2577,7 @@
  * return NULL if it doesn't recognize the error code.  We don't
  * want to return NULL ever.
  */
-static const char *
+const char *
 SSLerrmessage(void)
 {
 	unsigned long	errcode;
@@ -2695,3 +2798,380 @@
 	va_end(ap);
 	fprintf(stderr, "\n");
 }
+
+#ifdef USE_SSL
+/*
+ *	Null authentication callback
+ */
+static int verifyCB (int ok, X509_STORE_CTX *ctx)
+{
+	char buf[256], buf1[256];
+	X509 *cert;
+	int err, depth, n;
+	BIO *bio;
+
+	cert = X509_STORE_CTX_get_current_cert(ctx);
+	err  = X509_STORE_CTX_get_error(ctx);
+	depth= X509_STORE_CTX_get_error_depth(ctx);
+
+	X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof buf);
+	if (!ok) 
+	{
+		switch (err)
+		{
+		/* accept self-signed certs */
+		case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
+			ok = 1;
+			break;
+
+		default:
+			elog(ERROR,"client cert %s: %s", buf,
+				X509_verify_cert_error_string(err));
+		}
+	}
+
+	switch(ctx->error)
+	{
+	case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+		X509_NAME_oneline(X509_get_issuer_name(cert), buf1, sizeof buf1);
+		elog(DEBUG, "client cert %s: cannot find issuer %s", buf, buf1);	
+		break;
+	case X509_V_ERR_CERT_NOT_YET_VALID:
+	case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
+		bio = BIO_new(BIO_s_mem());
+		ASN1_TIME_print(bio, X509_get_notBefore(cert));
+		BIO_flush(bio);
+		n = BIO_read(bio, buf1, sizeof(buf1)-1);
+		buf1[n] = '\0';
+		BIO_free(bio);
+		strncpy(buf, "(not yet translated)", sizeof buf);
+		elog(DEBUG, "client cert %s: not valid until %s", buf, buf1);
+		break;
+	case X509_V_ERR_CERT_HAS_EXPIRED:
+	case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
+		bio = BIO_new(BIO_s_mem());
+		ASN1_TIME_print(bio, X509_get_notAfter(cert));
+		BIO_flush(bio);
+		n = BIO_read(bio, buf1, sizeof(buf1)-1);
+		buf1[n] = '\0';
+		BIO_free(bio);
+		elog(DEBUG, "client cert %s: not valid after %s", buf, buf1);
+		break;
+	}
+
+	return ok;
+}
+
+/*
+ *	Load the DH parameters file, if it exists.
+ */
+static DH *load_dh_param(const char *filename)
+{
+	FILE *fp;
+	DH *dh = NULL;
+
+	if ((fp = fopen(filename, "r")) == NULL)
+		return NULL;
+
+	flock(fileno(fp), LOCK_SH);
+	dh = PEM_read_DHparams(fp, NULL, NULL, NULL);
+	flock(fileno(fp), LOCK_UN);
+	fclose(fp);
+
+	return dh;
+}
+
+/*
+ *	Load a private key.
+ *	For security reasons, we fail if the file is not a regular file,
+ *	if it's group- or world-accessible, etc.  We should also verify
+ *	the file's ownership.
+ */
+static EVP_PKEY *load_pkey(const char *filename)
+{
+	int fd;
+	FILE *fp;
+	EVP_PKEY *pkey = NULL;
+	struct stat buf1, buf2;
+
+	if (lstat(filename, &buf1) == -1)
+		return NULL;
+	if (!S_ISREG(buf1.st_mode))
+		return NULL;
+	if ((buf1.st_mode & (S_IRWXG | S_IRWXO)) != 0)
+		return NULL;
+	if ((fd = open(filename, O_RDONLY)) == -1)
+		return NULL;
+	if (fstat(fd, &buf2) == -1)
+		return NULL;
+	if (buf1.st_dev != buf2.st_dev || buf1.st_ino != buf2.st_ino)
+		return NULL;
+
+	fp = fdopen(fd, "r");
+	flock(fileno(fp), LOCK_SH);
+	pkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL);
+	flock(fileno(fp), LOCK_UN);
+	fclose(fp);
+
+	return pkey;
+}
+
+/*
+ *	Load a certificate.
+ */
+static X509 *load_cert(const char *filename)
+{
+	FILE *fp;
+	X509 *x = NULL;
+
+	if ((fp = fopen(filename, "r")) == NULL)
+		return NULL;
+
+	flock(fileno(fp), LOCK_SH);
+	x = PEM_read_X509(fp, NULL, NULL, NULL);
+	flock(fileno(fp), LOCK_UN);
+	fclose(fp);
+
+	return x;
+}
+
+/*
+ *	Generate a empheral DH key.  Because this can take a long
+ *	time to compute, we can use precomputed parameters of the
+ *	common key sizes. 
+ *
+ *	N.B., see the documentation for SSL_CTX_set_tmp_dh_callback()
+ *	for important notes about limitations if a DH parameter file
+ *	generated from DSA keys is used.
+ */
+static DH *tmp_dh_cb (SSL *s, int is_export, int keylength)
+{
+	static DH *dh512 = NULL;
+	static DH *dh1024 = NULL;
+	static DH *dh2048 = NULL;
+	static DH *dh4096 = NULL;
+	static DH *dh = NULL;
+	char fnbuf[2048];
+
+	if (keylength == 512)
+	{
+		if (dh512 == NULL)
+		{
+			snprintf(fnbuf, sizeof(fnbuf), "%s/dh512.pem", DataDir);
+			dh512 = load_dh_param(fnbuf);
+		}
+		if (dh512 == NULL)
+			dh512 = get_dh512();
+		return dh512;
+	}
+
+	if (keylength == 1024)
+	{
+		if (dh1024 == NULL)
+		{
+			snprintf(fnbuf, sizeof(fnbuf), "%s/dh1024.pem", DataDir);
+			dh1024 = load_dh_param(fnbuf);
+		}
+		if (dh1024 == NULL)
+			dh1024 = get_dh1024();
+		return dh1024;
+	}
+
+	if (keylength == 2048)
+	{
+		if (dh2048 == NULL)
+		{
+			snprintf(fnbuf, sizeof(fnbuf), "%s/dh2048.pem", DataDir);
+			dh2048 = load_dh_param(fnbuf);
+		}
+		if (dh2048 == NULL)
+			dh2048 = get_dh2048();
+		return dh2048;
+	}
+
+	if (keylength == 4096)
+	{
+		if (dh4096 == NULL)
+		{
+			snprintf(fnbuf, sizeof(fnbuf), "%s/dh4096.pem", DataDir);
+			dh4096 = load_dh_param(fnbuf);
+		}
+		if (dh4096 == NULL)
+			dh4096 = get_dh4096();
+		return dh4096;
+	}
+
+	if (dh == NULL)
+	{
+		dh = DH_generate_parameters(keylength, 2, NULL, NULL);
+	}
+
+	return dh;
+}
+
+/*
+ *	It is *highly* recommended that sites generate their own emphemeral
+ *	DH parameters files with
+ *
+ *	   openssl dhparams -2 -rand /dev/random -out dh512.pem 512
+ *
+ *	and the like during installation, with these files written into the
+ *	standard DataDir.
+ *
+ *	But since we all know that many sites won't bother, these routines
+ *	provide fallback DH parameters distributed by the OpenSSL project.
+ */
+#ifndef HEADER_DH_H
+#include <openssl/dh.h>
+#endif
+
+static DH *get_dh512()
+	{
+	static unsigned char dh512_p[]={
+		0xF5,0x2A,0xFF,0x3C,0xE1,0xB1,0x29,0x40,0x18,0x11,0x8D,0x7C,
+		0x84,0xA7,0x0A,0x72,0xD6,0x86,0xC4,0x03,0x19,0xC8,0x07,0x29,
+		0x7A,0xCA,0x95,0x0C,0xD9,0x96,0x9F,0xAB,0xD0,0x0A,0x50,0x9B,
+		0x02,0x46,0xD3,0x08,0x3D,0x66,0xA4,0x5D,0x41,0x9F,0x9C,0x7C,
+		0xBD,0x89,0x4B,0x22,0x19,0x26,0xBA,0xAB,0xA2,0x5E,0xC3,0x55,
+		0xE9,0x2A,0x05,0x5F,
+		};
+	static unsigned char dh512_g[]={
+		0x02,
+		};
+	DH *dh;
+
+	if ((dh=DH_new()) == NULL) return(NULL);
+	dh->p=BN_bin2bn(dh512_p,sizeof(dh512_p),NULL);
+	dh->g=BN_bin2bn(dh512_g,sizeof(dh512_g),NULL);
+	if ((dh->p == NULL) || (dh->g == NULL))
+		{ DH_free(dh); return(NULL); }
+	return(dh);
+	}
+
+static DH *get_dh1024()
+	{
+	static unsigned char dh1024_p[]={
+		0xF4,0x88,0xFD,0x58,0x4E,0x49,0xDB,0xCD,0x20,0xB4,0x9D,0xE4,
+		0x91,0x07,0x36,0x6B,0x33,0x6C,0x38,0x0D,0x45,0x1D,0x0F,0x7C,
+		0x88,0xB3,0x1C,0x7C,0x5B,0x2D,0x8E,0xF6,0xF3,0xC9,0x23,0xC0,
+		0x43,0xF0,0xA5,0x5B,0x18,0x8D,0x8E,0xBB,0x55,0x8C,0xB8,0x5D,
+		0x38,0xD3,0x34,0xFD,0x7C,0x17,0x57,0x43,0xA3,0x1D,0x18,0x6C,
+		0xDE,0x33,0x21,0x2C,0xB5,0x2A,0xFF,0x3C,0xE1,0xB1,0x29,0x40,
+		0x18,0x11,0x8D,0x7C,0x84,0xA7,0x0A,0x72,0xD6,0x86,0xC4,0x03,
+		0x19,0xC8,0x07,0x29,0x7A,0xCA,0x95,0x0C,0xD9,0x96,0x9F,0xAB,
+		0xD0,0x0A,0x50,0x9B,0x02,0x46,0xD3,0x08,0x3D,0x66,0xA4,0x5D,
+		0x41,0x9F,0x9C,0x7C,0xBD,0x89,0x4B,0x22,0x19,0x26,0xBA,0xAB,
+		0xA2,0x5E,0xC3,0x55,0xE9,0x2F,0x78,0xC7,
+		};
+	static unsigned char dh1024_g[]={
+		0x02,
+		};
+	DH *dh;
+
+	if ((dh=DH_new()) == NULL) return(NULL);
+	dh->p=BN_bin2bn(dh1024_p,sizeof(dh1024_p),NULL);
+	dh->g=BN_bin2bn(dh1024_g,sizeof(dh1024_g),NULL);
+	if ((dh->p == NULL) || (dh->g == NULL))
+		{ DH_free(dh); return(NULL); }
+	return(dh);
+	}
+
+static DH *get_dh2048()
+	{
+	static unsigned char dh2048_p[]={
+		0xF6,0x42,0x57,0xB7,0x08,0x7F,0x08,0x17,0x72,0xA2,0xBA,0xD6,
+		0xA9,0x42,0xF3,0x05,0xE8,0xF9,0x53,0x11,0x39,0x4F,0xB6,0xF1,
+		0x6E,0xB9,0x4B,0x38,0x20,0xDA,0x01,0xA7,0x56,0xA3,0x14,0xE9,
+		0x8F,0x40,0x55,0xF3,0xD0,0x07,0xC6,0xCB,0x43,0xA9,0x94,0xAD,
+		0xF7,0x4C,0x64,0x86,0x49,0xF8,0x0C,0x83,0xBD,0x65,0xE9,0x17,
+		0xD4,0xA1,0xD3,0x50,0xF8,0xF5,0x59,0x5F,0xDC,0x76,0x52,0x4F,
+		0x3D,0x3D,0x8D,0xDB,0xCE,0x99,0xE1,0x57,0x92,0x59,0xCD,0xFD,
+		0xB8,0xAE,0x74,0x4F,0xC5,0xFC,0x76,0xBC,0x83,0xC5,0x47,0x30,
+		0x61,0xCE,0x7C,0xC9,0x66,0xFF,0x15,0xF9,0xBB,0xFD,0x91,0x5E,
+		0xC7,0x01,0xAA,0xD3,0x5B,0x9E,0x8D,0xA0,0xA5,0x72,0x3A,0xD4,
+		0x1A,0xF0,0xBF,0x46,0x00,0x58,0x2B,0xE5,0xF4,0x88,0xFD,0x58,
+		0x4E,0x49,0xDB,0xCD,0x20,0xB4,0x9D,0xE4,0x91,0x07,0x36,0x6B,
+		0x33,0x6C,0x38,0x0D,0x45,0x1D,0x0F,0x7C,0x88,0xB3,0x1C,0x7C,
+		0x5B,0x2D,0x8E,0xF6,0xF3,0xC9,0x23,0xC0,0x43,0xF0,0xA5,0x5B,
+		0x18,0x8D,0x8E,0xBB,0x55,0x8C,0xB8,0x5D,0x38,0xD3,0x34,0xFD,
+		0x7C,0x17,0x57,0x43,0xA3,0x1D,0x18,0x6C,0xDE,0x33,0x21,0x2C,
+		0xB5,0x2A,0xFF,0x3C,0xE1,0xB1,0x29,0x40,0x18,0x11,0x8D,0x7C,
+		0x84,0xA7,0x0A,0x72,0xD6,0x86,0xC4,0x03,0x19,0xC8,0x07,0x29,
+		0x7A,0xCA,0x95,0x0C,0xD9,0x96,0x9F,0xAB,0xD0,0x0A,0x50,0x9B,
+		0x02,0x46,0xD3,0x08,0x3D,0x66,0xA4,0x5D,0x41,0x9F,0x9C,0x7C,
+		0xBD,0x89,0x4B,0x22,0x19,0x26,0xBA,0xAB,0xA2,0x5E,0xC3,0x55,
+		0xE9,0x32,0x0B,0x3B,
+		};
+	static unsigned char dh2048_g[]={
+		0x02,
+		};
+	DH *dh;
+
+	if ((dh=DH_new()) == NULL) return(NULL);
+	dh->p=BN_bin2bn(dh2048_p,sizeof(dh2048_p),NULL);
+	dh->g=BN_bin2bn(dh2048_g,sizeof(dh2048_g),NULL);
+	if ((dh->p == NULL) || (dh->g == NULL))
+		{ DH_free(dh); return(NULL); }
+	return(dh);
+	}
+
+static DH *get_dh4096()
+	{
+	static unsigned char dh4096_p[]={
+		0xFA,0x14,0x72,0x52,0xC1,0x4D,0xE1,0x5A,0x49,0xD4,0xEF,0x09,
+		0x2D,0xC0,0xA8,0xFD,0x55,0xAB,0xD7,0xD9,0x37,0x04,0x28,0x09,
+		0xE2,0xE9,0x3E,0x77,0xE2,0xA1,0x7A,0x18,0xDD,0x46,0xA3,0x43,
+		0x37,0x23,0x90,0x97,0xF3,0x0E,0xC9,0x03,0x50,0x7D,0x65,0xCF,
+		0x78,0x62,0xA6,0x3A,0x62,0x22,0x83,0xA1,0x2F,0xFE,0x79,0xBA,
+		0x35,0xFF,0x59,0xD8,0x1D,0x61,0xDD,0x1E,0x21,0x13,0x17,0xFE,
+		0xCD,0x38,0x87,0x9E,0xF5,0x4F,0x79,0x10,0x61,0x8D,0xD4,0x22,
+		0xF3,0x5A,0xED,0x5D,0xEA,0x21,0xE9,0x33,0x6B,0x48,0x12,0x0A,
+		0x20,0x77,0xD4,0x25,0x60,0x61,0xDE,0xF6,0xB4,0x4F,0x1C,0x63,
+		0x40,0x8B,0x3A,0x21,0x93,0x8B,0x79,0x53,0x51,0x2C,0xCA,0xB3,
+		0x7B,0x29,0x56,0xA8,0xC7,0xF8,0xF4,0x7B,0x08,0x5E,0xA6,0xDC,
+		0xA2,0x45,0x12,0x56,0xDD,0x41,0x92,0xF2,0xDD,0x5B,0x8F,0x23,
+		0xF0,0xF3,0xEF,0xE4,0x3B,0x0A,0x44,0xDD,0xED,0x96,0x84,0xF1,
+		0xA8,0x32,0x46,0xA3,0xDB,0x4A,0xBE,0x3D,0x45,0xBA,0x4E,0xF8,
+		0x03,0xE5,0xDD,0x6B,0x59,0x0D,0x84,0x1E,0xCA,0x16,0x5A,0x8C,
+		0xC8,0xDF,0x7C,0x54,0x44,0xC4,0x27,0xA7,0x3B,0x2A,0x97,0xCE,
+		0xA3,0x7D,0x26,0x9C,0xAD,0xF4,0xC2,0xAC,0x37,0x4B,0xC3,0xAD,
+		0x68,0x84,0x7F,0x99,0xA6,0x17,0xEF,0x6B,0x46,0x3A,0x7A,0x36,
+		0x7A,0x11,0x43,0x92,0xAD,0xE9,0x9C,0xFB,0x44,0x6C,0x3D,0x82,
+		0x49,0xCC,0x5C,0x6A,0x52,0x42,0xF8,0x42,0xFB,0x44,0xF9,0x39,
+		0x73,0xFB,0x60,0x79,0x3B,0xC2,0x9E,0x0B,0xDC,0xD4,0xA6,0x67,
+		0xF7,0x66,0x3F,0xFC,0x42,0x3B,0x1B,0xDB,0x4F,0x66,0xDC,0xA5,
+		0x8F,0x66,0xF9,0xEA,0xC1,0xED,0x31,0xFB,0x48,0xA1,0x82,0x7D,
+		0xF8,0xE0,0xCC,0xB1,0xC7,0x03,0xE4,0xF8,0xB3,0xFE,0xB7,0xA3,
+		0x13,0x73,0xA6,0x7B,0xC1,0x0E,0x39,0xC7,0x94,0x48,0x26,0x00,
+		0x85,0x79,0xFC,0x6F,0x7A,0xAF,0xC5,0x52,0x35,0x75,0xD7,0x75,
+		0xA4,0x40,0xFA,0x14,0x74,0x61,0x16,0xF2,0xEB,0x67,0x11,0x6F,
+		0x04,0x43,0x3D,0x11,0x14,0x4C,0xA7,0x94,0x2A,0x39,0xA1,0xC9,
+		0x90,0xCF,0x83,0xC6,0xFF,0x02,0x8F,0xA3,0x2A,0xAC,0x26,0xDF,
+		0x0B,0x8B,0xBE,0x64,0x4A,0xF1,0xA1,0xDC,0xEE,0xBA,0xC8,0x03,
+		0x82,0xF6,0x62,0x2C,0x5D,0xB6,0xBB,0x13,0x19,0x6E,0x86,0xC5,
+		0x5B,0x2B,0x5E,0x3A,0xF3,0xB3,0x28,0x6B,0x70,0x71,0x3A,0x8E,
+		0xFF,0x5C,0x15,0xE6,0x02,0xA4,0xCE,0xED,0x59,0x56,0xCC,0x15,
+		0x51,0x07,0x79,0x1A,0x0F,0x25,0x26,0x27,0x30,0xA9,0x15,0xB2,
+		0xC8,0xD4,0x5C,0xCC,0x30,0xE8,0x1B,0xD8,0xD5,0x0F,0x19,0xA8,
+		0x80,0xA4,0xC7,0x01,0xAA,0x8B,0xBA,0x53,0xBB,0x47,0xC2,0x1F,
+		0x6B,0x54,0xB0,0x17,0x60,0xED,0x79,0x21,0x95,0xB6,0x05,0x84,
+		0x37,0xC8,0x03,0xA4,0xDD,0xD1,0x06,0x69,0x8F,0x4C,0x39,0xE0,
+		0xC8,0x5D,0x83,0x1D,0xBE,0x6A,0x9A,0x99,0xF3,0x9F,0x0B,0x45,
+		0x29,0xD4,0xCB,0x29,0x66,0xEE,0x1E,0x7E,0x3D,0xD7,0x13,0x4E,
+		0xDB,0x90,0x90,0x58,0xCB,0x5E,0x9B,0xCD,0x2E,0x2B,0x0F,0xA9,
+		0x4E,0x78,0xAC,0x05,0x11,0x7F,0xE3,0x9E,0x27,0xD4,0x99,0xE1,
+		0xB9,0xBD,0x78,0xE1,0x84,0x41,0xA0,0xDF,
+		};
+	static unsigned char dh4096_g[]={
+		0x02,
+		};
+	DH *dh;
+
+	if ((dh=DH_new()) == NULL) return(NULL);
+	dh->p=BN_bin2bn(dh4096_p,sizeof(dh4096_p),NULL);
+	dh->g=BN_bin2bn(dh4096_g,sizeof(dh4096_g),NULL);
+	if ((dh->p == NULL) || (dh->g == NULL))
+		{ DH_free(dh); return(NULL); }
+	return(dh);
+	}
+#endif
