WIP: Secure Transport support as OpenSSL alternative on macOS

Started by Daniel Gustafssonover 9 years ago6 messages
#1Daniel Gustafsson
daniel@yesql.se
3 attachment(s)

As noted in 77FB2321-9210-4AD4-B7FC-67623A5838F0@justatheory.com, OpenSSL is
being somewhat dismantled on macOS starting with 10.11 El Capitan. To scratch
my own itch I decided to take a stab at adding support for the Secure Transport
library. The idea is to make it as much of a drop-in for the OpenSSL support
as possible, supporting the same file formats etc (Secure Transport have PKCS12
API calls while support PEM requires using lower level APIs).

Only the frontend has been implemented so far. I used a vanilla OpenSSL-
compiled backend for testing (see patch 0002 below). Currently the CRL tests
fail since Secure Transport doesn’t handle manual CRL lists like OpenSSL, CRL
handling is done automatically by the Keychain upon validation. One
alternative for supporting this could perhaps be to create a temporary Keychain
for the duration of the connection over which more control can be had (need to
research this further). For now, no Secure Transport specifics are supported
but the idea is to add support for loading certificates and keys from the
Keychain as well as from the usual locations. The engine and compression
settings will however be no-ops since there is no support in Secure Transport
for these.

Being an early WIP show-and-tell submissions and not something up for code
review (anyone wanting to take a look is of course more than welcome), it is
deliberately rough around the edges. Except the manual CRL loading, it’s
mostly featurecomplete with the OpenSSL frontend, TODO items include: removing
use of private and deprecated API calls; ensure release of all resources;
support certificates/keys in Keychain; possibly use temporary keychains for
connections; support passphrase entering (there has been lots of deprecations
around that, need to figure out the supported way forward); tidy up, and write
better, comments; documentation.

The main questions raised here are: is it of interest to support multiple SSL
libraries given the additional support burden and; is supporting Secure
Transport of any interest or is it a wasted effort to continue towards a full
frontend/backend/doc submission?

On top of the OpenSSL situation in macOS, supporting Secure Transport makes
sense if the Keychain can be fully leveraged since that would allow IT
organisations to centrally manage and provision Mac clients without passing
certificate/key files around. Should there be any interest in this I intend to
continue on one more library which is more geared towards server environments
like nss or <suggestions-welcome> once Secure Transport is done (scratching
another itch).

The attached show-and-tell patches are:

0001. The WIP frontend support for Secure Transport together with minimal
scaffolding around it such as Makefile and autoconf.

0002. Adding support to PostgresNode.pm to run the server binaries from a
defined path making it easier to test a frontend without having to a)
implement the backend at the same time or b) fiddle with paths manually.
The SSL tests are the only ones modified. There is nothing Secure Transport
specific in this patch so iff the approach is deemed interesting it could be
considered out of band. Since the only consumer is testing SSL during
development it might be too invasive however.

0003. A first little stab at tweaking the docs to mention support for
multiple SSL libraries. There is a lot left here but the idea is to contain
the library specifics in subsections with the main sections being generic
SSL support information. On top of tweaking the existing sections I intend
(once done) to write up the steps needed to implement support for an SSL
library, this should perhaps be a README in the tree though?

Thoughts, comments?

cheers ./daniel

Attachments:

0001-WIP-Add-support-for-macOS-Secure-Transport-SSL-libra.patchapplication/octet-stream; name=0001-WIP-Add-support-for-macOS-Secure-Transport-SSL-libra.patchDownload
From 649755518f9872e9cadd421434396ea999d75229 Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <daniel@yesql.se>
Date: Tue, 4 Oct 2016 14:41:39 +0200
Subject: [PATCH 1/3] WIP: Add support for macOS Secure Transport SSL library

This is a work in progress trying to add full support for Secure
Transport to the front- and backend. Currently only the frontend
is worked on in an attempt to start small (or rather, somewhere).
---
 configure.in                                     |   14 +
 src/Makefile.global.in                           |    1 +
 src/include/pg_config.h.in                       |    3 +
 src/include/pg_config_manual.h                   |    2 +-
 src/interfaces/libpq/Makefile                    |    5 +
 src/interfaces/libpq/fe-connect.c                |    4 +
 src/interfaces/libpq/fe-secure-securetransport.c | 1589 ++++++++++++++++++++++
 src/interfaces/libpq/libpq-int.h                 |   17 +
  8 files changed, 1634 insertions(+), 1 deletion(-)
 create mode 100644 src/interfaces/libpq/fe-secure-securetransport.c

diff --git a/configure.in b/configure.in
index 9850d99..7506bfb 100644
--- a/configure.in
+++ b/configure.in
@@ -713,6 +713,15 @@ AC_MSG_RESULT([$with_openssl])
 AC_SUBST(with_openssl)
 
 #
+# macOS Secure Transport
+#
+AC_MSG_CHECKING([whether to build with macOS Secure Transport support])
+PGAC_ARG_BOOL(with, securetransport, no, [build with macOS Secure Transport support],
+			  [AC_DEFINE([USE_SECURETRANSPORT], 1, [Define to build with macOS Secure Transport support.  (--with-securetransport)])])
+AC_MSG_RESULT([$with_securetransport])
+AC_SUBST(with_securetransport)
+
+#
 # SELinux
 #
 AC_MSG_CHECKING([whether to build with SELinux support])
@@ -1283,6 +1292,11 @@ if test "$with_openssl" = yes ; then
   AC_CHECK_HEADER(openssl/err.h, [], [AC_MSG_ERROR([header file <openssl/err.h> is required for OpenSSL])])
 fi
 
+if test "$with_securetransport" = yes ; then
+  AC_CHECK_HEADER(Security/Security.h, [], [AC_MSG_ERROR([header file <Security/Security.h> is required for Secure Transport])])
+  AC_CHECK_HEADER(CoreFoundation/CoreFoundation.h, [], [AC_MSG_ERROR([header file <CoreFoundation/CoreFoundation.h> is required for Secure Transport])])
+fi
+
 if test "$with_pam" = yes ; then
   AC_CHECK_HEADERS(security/pam_appl.h, [],
                    [AC_CHECK_HEADERS(pam/pam_appl.h, [],
diff --git a/src/Makefile.global.in b/src/Makefile.global.in
index c211a2d..54a4651 100644
--- a/src/Makefile.global.in
+++ b/src/Makefile.global.in
@@ -183,6 +183,7 @@ with_perl	= @with_perl@
 with_python	= @with_python@
 with_tcl	= @with_tcl@
 with_openssl	= @with_openssl@
+with_securetransport = @with_securetransport@
 with_selinux	= @with_selinux@
 with_systemd	= @with_systemd@
 with_libxml	= @with_libxml@
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 7dbfa90..cf4df0d 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -842,6 +842,9 @@
 /* Define to build with OpenSSL support. (--with-openssl) */
 #undef USE_OPENSSL
 
+/* Define to build with Apple Secure Transport support. (--with-securetransport) */
+#undef USE_SECURETRANSPORT
+
 /* Define to 1 to build with PAM support. (--with-pam) */
 #undef USE_PAM
 
diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h
index a2b2b61..357ebbb 100644
--- a/src/include/pg_config_manual.h
+++ b/src/include/pg_config_manual.h
@@ -152,7 +152,7 @@
  * implementation.  (Currently, only OpenSSL is supported, but we might add
  * more implementations in the future.)
  */
-#ifdef USE_OPENSSL
+#if defined(USE_OPENSSL) || defined(USE_SECURETRANSPORT)
 #define USE_SSL
 #endif
 
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index b1789eb..b8aed5a 100644
--- a/src/interfaces/libpq/Makefile
+++ b/src/interfaces/libpq/Makefile
@@ -48,6 +48,11 @@ ifeq ($(with_openssl),yes)
 OBJS += fe-secure-openssl.o
 endif
 
+ifeq ($(with_securetransport), yes)
+OBJS += fe-secure-securetransport.o
+override CFLAGS += -framework Security -framework CoreFoundation -fconstant-cfstrings
+endif
+
 ifeq ($(PORTNAME), cygwin)
 override shlib = cyg$(NAME)$(DLSUFFIX)
 endif
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index f3a9e5a..e141786 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -254,6 +254,10 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
 		"SSL-Mode", "", 12,		/* sizeof("verify-full") == 12 */
 	offsetof(struct pg_conn, sslmode)},
 
+	/*
+	 * SSL Compression is only available in OpenSSL enabled builds making this
+	 * option a no op when using other SSL libraries.
+	 */
 	{"sslcompression", "PGSSLCOMPRESSION", "1", NULL,
 		"SSL-Compression", "", 1,
 	offsetof(struct pg_conn, sslcompression)},
diff --git a/src/interfaces/libpq/fe-secure-securetransport.c b/src/interfaces/libpq/fe-secure-securetransport.c
new file mode 100644
index 0000000..1b3772e
--- /dev/null
+++ b/src/interfaces/libpq/fe-secure-securetransport.c
@@ -0,0 +1,1589 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-secure-securetransport.c
+ *	  Secure Transport support
+ *
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/interfaces/libpq/fe-secure-securetransport.c
+ *
+ * NOTES
+ *	  Unlike the OpenSSL support there is no shared state between connections
+ *	  so there is no special handling for ENABLE_THREAD_SAFETY.
+ *
+ * TODO:
+ *		- CRL support
+ *			- CRL is handled by the Keychain and there is no (obvious) way to
+ *			  inject x509 CRL records programatically. Perhaps creating a temp
+ *			  keychain for the connection and see if we can manipulate that?
+ *		- Load certificate/key from keychain
+ *			- Currently the certificate/key are expected to be files on disk
+ *			  but with a good prefix like keychain:foo we can load directly
+ *			  from the keychain the user wants.
+ *		- Supprt pkcs12 certificates
+ *		- Remove use of private API
+ *		- Remove use of deprecated functions
+ *		- pgcrypto
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres_fe.h"
+
+#include <signal.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#include "libpq-fe.h"
+#include "fe-auth.h"
+#include "libpq-int.h"
+
+#include <sys/socket.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+#include <arpa/inet.h>
+
+#include <sys/stat.h>
+
+#include <Security/Security.h>
+#include <Security/SecureTransport.h>
+#include <CoreFoundation/CoreFoundation.h>
+
+/*
+ * The number of required key/value pairs in the dictionary used for querying
+ * the Keychain: Class, Return Reference, MatchLimit, MatchPolicy
+ */
+#define KEYCHAIN_SEARCH_SIZE 4
+
+/*
+ * Private API call used in the Webkit code for creating an identity from a
+ * certificate with a key. While stable and used in many open source projects
+ * it should be replaced with a published API call since private APIs aren't
+ * subject to the same deprecation rules. Could potentially be replaced by
+ * using SecIdentityCreateWithCertificate() ?
+ */
+extern SecIdentityRef SecIdentityCreate(CFAllocatorRef allocator,
+										SecCertificateRef certificate,
+										SecKeyRef privateKey);
+
+static char *SSLerrmessage(OSStatus errcode);
+static void SSLerrfree(char *err_buf);
+static int SSLsessionstate(PGconn *conn, char *msg, size_t len);
+static const char * SSLciphername(SSLCipherSuite cipher);
+
+static OSStatus SSLSocketRead(SSLConnectionRef conn, void *data,
+							  size_t *len);
+static OSStatus SSLSocketWrite(SSLConnectionRef conn, const void *data,
+							   size_t *len);
+static OSStatus SSLOpenClient(PGconn *conn);
+static OSStatus SSLLoadCertificate(PGconn *conn, CFArrayRef *cert_array,
+								   CFArrayRef *key_array,
+								   CFArrayRef *rootcert_array);
+
+static OSStatus import_certificate(const char *path, int size,
+								   SecExternalFormat format, char *passphrase,
+								   CFArrayRef *certificate);
+static OSStatus import_pem(const char *path, int size, char *passphrase,
+						   CFArrayRef *cert_arr);
+static OSStatus import_pkcs12(const char *path, int size, char *passphrase,
+							  CFArrayRef *cert_arr);
+
+/* ------------------------------------------------------------ */
+/*						 Public interface						*/
+/* ------------------------------------------------------------ */
+
+/*
+ *	Exported function to allow application to tell us it's already
+ *	initialized Secure Transport and/or libcrypto.
+ */
+void
+pgtls_init_library(bool do_ssl, int do_crypto)
+{
+#ifndef __darwin__
+	/*
+	 * Secure Transport is only available on Darwin platforms so autoconf
+	 * should protect us from ever reaching here
+	 */
+	Assert(false);
+#endif
+}
+
+/*
+ *	Begin or continue negotiating a secure session.
+ */
+PostgresPollingStatusType
+pgtls_open_client(PGconn *conn)
+{
+	OSStatus open_status;
+
+	CFArrayRef certificate;
+	CFArrayRef key;
+	CFArrayRef rootcert;
+
+	/*
+	 * If the SSL context hasn't been set up then initiate it, else continue
+	 * with handshake
+	 */
+	if (conn->ssl == NULL)
+	{
+		conn->ssl_key_bits = 0;
+		conn->ssl_buffered = 0;
+		/*
+		 * Create the SSL context using the new API introduced in 10.8 since
+		 * the SSLNewContext() API call was deprecated in 10.9. The standard
+		 * allocator is used since we are client side.
+		 */
+		conn->ssl = SSLCreateContext(NULL /* allocator */,
+									 kSSLClientSide, kSSLStreamType);
+		if (!conn->ssl)
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+				   libpq_gettext("could not create SSL context\n"));
+			return PGRES_POLLING_FAILED;
+		}
+		/*
+		 * SSLSetProtocolVersionEnabled() is marked as deprecated as of 10.9
+		 * but the alternative SSLSetSessionConfig() is as of 10.11 not yet
+		 * documented with the kSSLSessionConfig_xxx constants belonging to
+		 * the 10.12 SDK. Rely on the deprecation for now until the dust has
+		 * properly settled around this.
+		 */
+		SSLSetProtocolVersionEnabled(conn->ssl, kTLSProtocol12, true);
+
+		open_status = SSLSetConnection(conn->ssl, conn);
+		if (open_status != noErr)
+			goto error;
+
+		/*
+		 * Set the low level functions for reading and writing off a socket
+		 */
+		open_status = SSLSetIOFuncs(conn->ssl, SSLSocketRead, SSLSocketWrite);
+		if (open_status != noErr)
+			goto error;
+
+		/*
+		 * Load client certificate, private key, and trusted CA certs. The
+		 * conn->errorMessage will be populated by the certificate loading
+		 * so we can return without altering it.
+		 */
+		if (SSLLoadCertificate(conn, &certificate, &key, &rootcert) != noErr)
+		{
+			pgtls_close(conn);
+			return PGRES_POLLING_FAILED;
+		}
+
+		conn->st_rootcert = CFRetain(rootcert);
+
+		/*
+		 * If we are asked to verify the peer hostname, set it as a requirement
+		 * on the connection. This must be set before calling SSLHandshake().
+		 */
+		if (strcmp(conn->sslmode, "verify-full") == 0)
+		{
+			/* If we are asked to verify a hostname we dont have, error out */
+			if (!conn->pghost)
+			{
+				pgtls_close(conn);
+				return PGRES_POLLING_FAILED;
+			}
+
+			SSLSetPeerDomainName(conn->ssl, conn->pghost, strlen(conn->pghost));
+		}
+	}
+
+	/*
+	 * Perform handshake
+	 */
+	open_status = SSLOpenClient(conn);
+	if (open_status == noErr)
+	{
+		conn->ssl_in_use = true;
+		return PGRES_POLLING_OK;
+	}
+
+error:
+	if (open_status != noErr)
+	{
+		char *err_msg = SSLerrmessage(open_status);
+		if (conn->errorMessage.len > 0)
+			appendPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext(", ssl error: %s\n"), err_msg);
+		else
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("could not establish SSL connection: %s\n"),
+									err_msg);
+		SSLerrfree(err_msg);
+
+		pgtls_close(conn);
+	}
+
+	return PGRES_POLLING_FAILED;
+}
+
+/*
+ * SSLOpenClient
+ *		Validates remote certificate and performs handshake.
+ *
+ * If the user has supplied a root certificate we add that to the chain here
+ * before initiating validation. The caller is responsible for invoking error
+ * logging in the case of errors returned.
+ */
+static OSStatus
+SSLOpenClient(PGconn *conn)
+{
+	OSStatus			status;
+	SecTrustRef			trust = NULL;
+	SecTrustResultType	trust_eval = 0;
+	bool				trusted = false;
+	bool				only_anchor = true;
+
+	SSLSetSessionOption(conn->ssl, kSSLSessionOptionBreakOnServerAuth, true);
+
+	/*
+	 * TODO: Is there a better way to repeatedly call SSLHandshake until we get
+	 * another response than an errSSLWouldBlock?
+	 */
+	do
+	{
+		status = SSLHandshake(conn->ssl);
+		/* busy-wait loop */
+	}
+	while (status == errSSLWouldBlock || status == -1);
+
+	if (status != errSSLServerAuthCompleted)
+		return status;
+
+	/*
+	 * Get peer server certificate and validate it. SSLCopyPeerTrust() is not
+	 * supposed to return a NULL trust on noErr but have been reported to do
+	 * in the past so add a belts-and-suspenders check
+	 */
+	status = SSLCopyPeerTrust(conn->ssl, &trust);
+	if (status != noErr || trust == NULL)
+		return status;
+
+	/*
+	 * If we have our own root certificate configured then add it to the chain
+	 * of trust and specify that it should be trusted.
+	 */
+	if (conn->st_rootcert)
+	{
+		status = SecTrustSetAnchorCertificates(trust, conn->st_rootcert);
+		if (status != noErr)
+			return status;
+
+		/* We have a trusted local root cert, trust more than anchor */
+		only_anchor = false;
+	}
+
+	status = SecTrustSetAnchorCertificatesOnly(trust, only_anchor);
+	if (status != noErr)
+		return status;
+
+	status = SecTrustEvaluate(trust, &trust_eval);
+	if (status == errSecSuccess)
+	{
+		switch (trust_eval)
+		{
+			/*
+			 * If 'Unspecified' then an anchor certificate was reached without
+			 * encountering any explicit user trust. If 'Proceed' then the user
+			 * has chosen to explicitly trust a certificate in the chain by
+			 * clicking "Trust" in the Keychain app.
+			 */
+			case kSecTrustResultUnspecified:
+			case kSecTrustResultProceed:
+				trusted = true;
+				break;
+
+			/*
+			 * 'Confirm' indicates that an interactive confirmation from the
+			 * user is requested. This result code was deprecated in 10.9
+			 * however so treat it as a Deny to avoid having to invoke UI
+			 * elements from the Keychain.
+			 */
+			case kSecTrustResultConfirm:
+			/*
+			 * 'RecoverableTrustFailure' indicates that the certificate was
+			 * rejected but might be trusted with minor changes to the eval
+			 * context (ignoring expired certificate etc). TODO: Opening up to
+			 * changing the eval context here seems dangerous but we can do
+			 * better logging of the error by invoking SecTrustGetTrustResult()
+			 * to get info on exactly what failed; call and extract relevant
+			 * information to the logstring.
+			 */
+			case kSecTrustResultRecoverableTrustFailure:
+			/*
+			 * The below results are all cases where the certificate should be
+			 * rejected without further questioning.
+			 */
+			case kSecTrustResultDeny:
+			case kSecTrustResultFatalTrustFailure:
+			case kSecTrustResultOtherError:
+			default:
+				trusted = false;
+				break;
+		}
+	}
+
+	/*
+	 * TODO: return a better error code than SSLInternalError
+	 */
+	if (!trusted)
+		return errSecInternalError;
+
+	/*
+	 * If we reach here the documentation states we need to run the Handshake
+	 * again after validating the trust */
+	return SSLOpenClient(conn);
+}
+
+/*
+ *	Is there unread data waiting in the SSL read buffer?
+ */
+bool
+pgtls_read_pending(PGconn *conn)
+{
+	OSStatus read_status;
+	size_t len = 0;
+
+	read_status = SSLGetBufferedReadSize(conn->ssl, &len);
+
+	/*
+	 * Should we get an error back then we assume that subsequent read
+	 * operations will fail as well.
+	 */
+	return (read_status == noErr && len > 0);
+}
+
+/*
+ *  pgtls_read
+ *	    Read data from a secure connection.
+ *
+ * On failure, this function is responsible for putting a suitable message
+ * into conn->errorMessage.  The caller must still inspect errno, but only
+ * to determine whether to continue/retry after error.
+ */
+ssize_t
+pgtls_read(PGconn *conn, void *ptr, size_t len)
+{
+	OSStatus	read_status;
+	size_t		n = 0;
+	ssize_t		ret = 0;
+	int			read_errno = 0;
+	char		sess_msg[25];
+
+	/*
+	 * Double-check that we have a connection which is in the correct state
+	 * for reading before attempting to pull any data off the wire.
+	 */
+	if (SSLsessionstate(conn, sess_msg, sizeof(sess_msg)) == -1)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+			libpq_gettext("SSL connection is: %s\n"), sess_msg);
+		read_errno = ECONNRESET;
+		return -1;
+	}
+
+	read_status = SSLRead(conn->ssl, ptr, len, &n);
+	ret = (ssize_t) n;
+
+	switch (read_status)
+	{
+		case noErr:
+			break;
+		case -1:
+		case errSSLWouldBlock:
+			/* If we did perform a read then skip EAGAIN */
+			if (n == 0)
+				read_errno = EINTR;
+			break;
+
+		/*
+		 * Clean disconnections
+		 */
+		case errSSLClosedNoNotify:
+			/* fall through */
+		case errSSLClosedGraceful:
+			printfPQExpBuffer(&conn->errorMessage,
+				libpq_gettext("SSL connection has been closed unexpectedly\n"));
+			read_errno = ECONNRESET;
+			ret = -1;
+			break;
+
+		default:
+			printfPQExpBuffer(&conn->errorMessage,
+				libpq_gettext("unrecognized SSL error %d\n"), read_status);
+			read_errno = ECONNRESET;
+			ret = -1;
+			break;
+	}
+
+	SOCK_ERRNO_SET(read_errno);
+	return ret;
+}
+
+/*
+ *	Write data to a secure connection.
+ *
+ * On failure, this function is responsible for putting a suitable message
+ * into conn->errorMessage.  The caller must still inspect errno, but only
+ * to determine whether to continue/retry after error.
+ */
+ssize_t
+pgtls_write(PGconn *conn, const void *ptr, size_t len)
+{
+	OSStatus	write_status;
+	size_t		n = 0;
+	ssize_t		ret = 0;
+	int			write_errno = 0;
+	char		sess_msg[25];
+
+	/*
+	 * Double-check that we have a connection which is in the correct state
+	 * for writing before attempting to push any data on to the wire or the
+	 * local SSL buffer.
+	 */
+	if (SSLsessionstate(conn, sess_msg, sizeof(sess_msg)) == -1)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+			libpq_gettext("SSL connection is: %s\n"), sess_msg);
+		write_errno = ECONNRESET;
+		return -1;
+	}
+
+	if (conn->ssl_buffered > 0)
+	{
+		write_status = SSLWrite(conn->ssl, NULL, 0, &n);
+
+		if (write_status == noErr)
+		{
+			ret = conn->ssl_buffered;
+			conn->ssl_buffered = 0;
+		}
+		else if (write_status == errSSLWouldBlock || write_status == -1)
+		{
+			ret = 0;
+			write_errno = EINTR;
+		}
+		else
+		{
+			/* TODO: pull error message string, on read too */
+			printfPQExpBuffer(&conn->errorMessage,
+				libpq_gettext("unrecognized SSL error: %d\n"), write_status);
+			ret = -1;
+			write_errno = ECONNRESET;
+		}
+	}
+	else
+	{
+		write_status = SSLWrite(conn->ssl, ptr, len, &n);
+		ret = n;
+
+		switch (write_status)
+		{
+			case noErr:
+				break;
+
+			case -1:
+			case errSSLWouldBlock:
+				conn->ssl_buffered = len;
+				ret = 0;
+#ifdef EAGAIN
+				write_errno = EAGAIN;
+#else
+				write_errno = EINTR;
+#endif
+				break;
+
+			/*
+			 * Clean disconnections
+		 	*/
+			case errSSLClosedNoNotify:
+				/* fall through */
+			case errSSLClosedGraceful:
+				printfPQExpBuffer(&conn->errorMessage,
+					libpq_gettext("SSL connection has been closed unexpectedly\n"));
+				write_errno = ECONNRESET;
+				ret = -1;
+				break;
+
+			default:
+				printfPQExpBuffer(&conn->errorMessage,
+					libpq_gettext("unrecognized SSL error %d\n"), write_status);
+				write_errno = ECONNRESET;
+				ret = -1;
+				break;
+		}
+	}
+
+	SOCK_ERRNO_SET(write_errno);
+	return ret;
+}
+
+/*
+ * Initialize SSL system, in particular creating the SSL_context object
+ * that will be shared by all SSL-using connections in this process.
+ *
+ * In threadsafe mode, this includes setting up libcrypto callback functions
+ * to do thread locking.
+ *
+ * If the caller has told us (through PQinitOpenSSL) that he's taking care
+ * of libcrypto, we expect that callbacks are already set, and won't try to
+ * override it.
+ *
+ * The conn parameter is only used to be able to pass back an error
+ * message - no connection-local setup is made here.
+ *
+ * Returns 0 if OK, -1 on failure (with a message in conn->errorMessage).
+ */
+int
+pgtls_init(PGconn *conn)
+{
+	conn->ssl_buffered = 0;
+	conn->ssl_in_use = false;
+
+	return 0;
+}
+
+/*
+ *  pgtls_close
+ *	    Close SSL connection.
+ *
+ * This function must cope with connections in all states of disrepair since
+ * it will be called from pgtls_open_client to clean up any potentially used
+ * resources in case it breaks halfway.
+ */
+void
+pgtls_close(PGconn *conn)
+{
+	if (!conn->ssl)
+		return;
+
+	CFRelease(conn->st_rootcert);
+
+	SSLClose(conn->ssl);
+	CFRelease(conn->ssl);
+
+	/* TODO: Release any certificates loaded */
+
+	conn->ssl = NULL;
+	conn->ssl_in_use = false;
+}
+
+
+
+/*
+ * The amount of read bytes is returned in the len variable
+ */
+static OSStatus
+SSLSocketRead(SSLConnectionRef conn, void *data, size_t *len)
+{
+	OSStatus	status = noErr;
+	int			res;
+
+	res = pqsecure_raw_read((PGconn *) conn, data, *len);
+
+	if (res < 0)
+	{
+		/* TODO: Handle more error cases? */
+		switch (SOCK_ERRNO)
+		{
+			case ENOENT:
+				status = errSSLClosedGraceful;
+				break;
+
+#ifdef EAGAIN
+			case EAGAIN:
+#endif
+#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
+			case EWOULDBLOCK:
+#endif
+			case EINTR:
+				status = errSSLWouldBlock;
+				break;
+		}
+	}
+
+	*len = res;
+
+	return status;
+}
+
+static OSStatus
+SSLSocketWrite(SSLConnectionRef conn, const void *data, size_t *len)
+{
+	OSStatus	status = noErr;
+	int			res;
+
+	res = pqsecure_raw_write((PGconn *) conn, data, *len);
+
+	if (res < 0)
+	{
+		/* TODO: Handle more error cases? */
+		switch (SOCK_ERRNO)
+		{
+#ifdef EAGAIN
+			case EAGAIN:
+#endif
+#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
+			case EWOULDBLOCK:
+#endif
+			case EINTR:
+				status = errSSLWouldBlock;
+				break;
+
+			default:
+				break;
+		}
+	}
+
+	*len = res;
+
+	return status;
+}
+
+static OSStatus
+import_pem(const char *path, int size, char *passphrase, CFArrayRef *cert_arr)
+{
+	return import_certificate(path, size, kSecFormatPEMSequence, passphrase, cert_arr);
+}
+
+static OSStatus
+import_pkcs12(const char *path, int size, char *passphrase, CFArrayRef *cert_arr)
+{
+	return import_certificate(path, size, kSecFormatPKCS12, passphrase, cert_arr);
+}
+
+/*
+ * import_certificate_keychain
+ *
+ * Queries the local keychain for a certificate with the passed identity.
+ */
+static OSStatus
+import_certificate_keychain(const char *identity, SecIdentityRef *certificate)
+{
+	OSStatus		status = errSecItemNotFound;
+	CFTypeRef		key[KEYCHAIN_SEARCH_SIZE];
+	CFTypeRef		val[KEYCHAIN_SEARCH_SIZE];
+	CFDictionaryRef	identity_search;
+	CFStringRef		identity_ref;
+
+	identity_ref = CFStringCreateWithCString(NULL /* allocator */,
+											 identity, kCFStringEncodingUTF8);
+	key[0] = kSecClass;
+	val[0] = kSecClassIdentity;
+	key[1] = kSecReturnRef;
+	val[1] = kCFBooleanTrue;
+	key[2] = kSecMatchLimit;
+	val[2] = kSecMatchLimitOne;
+	key[3] = kSecMatchPolicy;
+	val[3] = SecPolicyCreateSSL(false, identity_ref);
+
+	identity_search = CFDictionaryCreate(NULL /* allocator */,
+										 (const void **) key,
+										 (const void **) val,
+										 KEYCHAIN_SEARCH_SIZE,
+										 &kCFCopyStringDictionaryKeyCallBacks,
+										 &kCFTypeDictionaryValueCallBacks);
+
+	status = SecItemCopyMatching(identity_search, (CFTypeRef *) certificate);
+
+	CFRelease(identity_search);
+	CFRelease(val[3]);
+	CFRelease(identity_ref);
+
+	return status;
+}
+
+static OSStatus
+import_certificate(const char *path, int size, SecExternalFormat format,
+				   char *passphrase, CFArrayRef *certificate)
+{
+	OSStatus							status;
+	CFDataRef							data_ref;
+	CFStringRef							file_type;
+	SecExternalItemType					item_type;
+	SecItemImportExportKeyParameters	params;
+
+	FILE *fp;
+	UInt8 *certdata;
+
+	Assert(path && strlen(path) > 0);
+
+	fp = fopen(path, "r");
+	if (!fp)
+		return errSecInternalError;
+
+	certdata = malloc(size);
+	if (!certdata)
+	{
+		fclose(fp);
+		return errSecAllocate;
+	}
+
+	/* TODO: should we use fopen()/fread() here or other fs abstractions */
+	if (fread(certdata, 1, size, fp) != size)
+	{
+		fclose(fp);
+		return errSecInternalError;
+	}
+	fclose(fp);
+
+	data_ref = CFDataCreate(NULL /* allocator */, certdata, size);
+
+	memset(&params, 0, sizeof(SecItemImportExportKeyParameters));
+	params.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
+	/* Set OS default access control on the imported key */
+	params.flags = kSecKeyNoAccessControl;
+	if (passphrase)
+		params.passphrase = CFStringCreateWithCString(NULL, passphrase,
+													  kCFStringEncodingUTF8);
+
+	/*
+	 * Provide a synthetic file ending for the certificate file to aid the
+	 * parsing of the certificate, the default name of ".crt" isn't terribly
+	 * helpful for figuring out the type
+	 */
+	if (format == kSecFormatPKCS12)
+		file_type = CFSTR(".pkcs12");
+	else
+		file_type = CFSTR(".pem");
+
+	item_type = kSecItemTypeCertificate;
+
+	status = SecItemImport(data_ref, file_type, &format, &item_type,
+						   0 /* flags */, &params, NULL /* keychain */,
+						   certificate);
+
+	if (status != errSecSuccess)
+		return status;
+
+	return status;
+}
+
+/*
+ * Since failures can come from multiple places, the PGconn errorMessage is
+ * populated here even for SSL library errors.
+ */
+static OSStatus
+SSLLoadCertificate(PGconn *conn, CFArrayRef *cert_array, CFArrayRef *key_array,
+				   CFArrayRef *rootcert_array)
+{
+	OSStatus			status;
+	struct stat 		buf;
+	char				homedir[MAXPGPATH];
+	char				fnbuf[MAXPGPATH];
+	char				sebuf[256];
+	bool				have_homedir;
+	bool				have_cert;
+	char	   		   *ssl_err_msg;
+	CFMutableArrayRef	cert_copy;
+	SecIdentityRef		identity;
+	SecCertificateRef	cert_ref;
+	SecKeyRef			key_ref;
+
+	/*
+	 * We'll need the home directory if any of the relevant parameters are
+	 * defaulted.  If pqGetHomeDirectory fails, act as though none of the
+	 * files could be found.
+	 */
+	if (!(conn->sslcert && strlen(conn->sslcert) > 0) ||
+		!(conn->sslkey && strlen(conn->sslkey) > 0) ||
+		!(conn->sslrootcert && strlen(conn->sslrootcert) > 0) ||
+		!(conn->sslcrl && strlen(conn->sslcrl) > 0))
+		have_homedir = pqGetHomeDirectory(homedir, sizeof(homedir));
+	else	/* won't need it */
+		have_homedir = false;
+
+	/* Read the client certificate file */
+	if (conn->sslcert && strlen(conn->sslcert) > 0)
+		strlcpy(fnbuf, conn->sslcert, sizeof(fnbuf));
+	else if (have_homedir)
+		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_CERT_FILE);
+	else
+		fnbuf[0] = '\0';
+
+	if (fnbuf[0] == '\0')
+	{
+		/* no home directory, proceed without a client cert */
+		have_cert = false;
+	}
+	else if (stat(fnbuf, &buf) != 0)
+	{
+		/*
+		 * If file is not present, just go on without a client cert; server
+		 * might or might not accept the connection.  Any other error,
+		 * however, is grounds for complaint.
+		 */
+		if (errno != ENOENT && errno != ENOTDIR)
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+			   libpq_gettext("could not open certificate file \"%s\": %s\n"),
+							  fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
+			return errSecInternalError;
+		}
+		have_cert = false;
+	}
+	else
+	{
+		status = import_pem(fnbuf, buf.st_size, NULL, cert_array);
+		if (status != noErr)
+		{
+			ssl_err_msg = SSLerrmessage(status);
+			printfPQExpBuffer(&conn->errorMessage,
+					libpq_gettext("could not load certificate file \"%s\": (%d) %s\n"),
+								  fnbuf, status, ssl_err_msg);
+			SSLerrfree(ssl_err_msg);
+			return status;
+		}
+
+		have_cert = true;
+	}
+
+	if (have_cert)
+	{
+		if (conn->sslkey && strlen(conn->sslkey) > 0)
+			strlcpy(fnbuf, conn->sslkey, sizeof(fnbuf));
+		else if (have_homedir)
+			snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_KEY_FILE);
+		else
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("certificate present, but private key file not found\n"));
+			return errSecInternalError;
+		}
+
+		if (stat(fnbuf, &buf) != 0)
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("certificate present, but not private key file \"%s\"\n"),
+							  fnbuf);
+			return errSecInternalError;
+		}
+
+		status = import_pem(fnbuf, buf.st_size, NULL, key_array);
+		if (status != noErr)
+		{
+			ssl_err_msg = SSLerrmessage(status);
+			printfPQExpBuffer(&conn->errorMessage,
+					libpq_gettext("could not load private key file \"%s\": %s\n"),
+								  fnbuf, ssl_err_msg);
+			SSLerrfree(ssl_err_msg);
+			return status;
+		}
+
+		cert_ref = (SecCertificateRef) CFArrayGetValueAtIndex(*cert_array, 0);
+		key_ref = (SecKeyRef) CFArrayGetValueAtIndex(*key_array, 0);
+
+		identity = SecIdentityCreate(NULL /* allocator */, cert_ref, key_ref);
+
+		cert_copy = CFArrayCreateMutableCopy(NULL /* allocator */, 0, *cert_array);
+		CFArraySetValueAtIndex(cert_copy, 0, identity);
+
+		/* FIXME: CFRelease whats not needed */
+
+		status = SSLSetCertificate(conn->ssl, cert_copy);
+
+		CFRelease(cert_copy);
+
+		if (status != noErr)
+		{
+			ssl_err_msg = SSLerrmessage(status);
+			printfPQExpBuffer(&conn->errorMessage,
+					libpq_gettext("could not set certificate for connection: (%d) %s\n"),
+								  status, ssl_err_msg);
+			SSLerrfree(ssl_err_msg);
+			return status;
+		}
+	}
+
+	/* Load the root cert */
+	if (conn->sslrootcert && strlen(conn->sslrootcert) > 0)
+		strlcpy(fnbuf, conn->sslrootcert, sizeof(fnbuf));
+	else if (have_homedir)
+		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CERT_FILE);
+	else
+		fnbuf[0] = '\0';
+
+	if (fnbuf[0] != '\0')
+	{
+		if (stat(fnbuf, &buf) != 0)
+		{
+			/*
+			 * stat() failed; assume root file doesn't exist.  If sslmode is
+			 * verify-ca or verify-full, this is an error.  Otherwise, continue
+			 * without performing any server cert verification.
+			 */
+			if (conn->sslmode[0] == 'v')	/* "verify-ca" or "verify-full" */
+			{
+				/*
+				 * The only way to reach here with an empty filename is if
+				 * pqGetHomeDirectory failed.  That's a sufficiently unusual case
+				 * that it seems worth having a specialized error message for it.
+				 */
+				if (fnbuf[0] == '\0')
+					printfPQExpBuffer(&conn->errorMessage,
+									  libpq_gettext("could not get home directory to locate root certificate file\n"
+													"Either provide the file or change sslmode to disable server certificate verification.\n"));
+				else
+					printfPQExpBuffer(&conn->errorMessage,
+						libpq_gettext("root certificate file \"%s\" does not exist\n"
+									  "Either provide the file or change sslmode to disable server certificate verification.\n"), fnbuf);
+				return errSecInternalError;
+			}
+		}
+		else
+		{
+			status = import_pem(fnbuf, buf.st_size, NULL, rootcert_array);
+			if (status != noErr)
+			{
+				ssl_err_msg = SSLerrmessage(status);
+				printfPQExpBuffer(&conn->errorMessage,
+						libpq_gettext("could not load root certificate file \"%s\": %s\n"),
+									  fnbuf, ssl_err_msg);
+				SSLerrfree(ssl_err_msg);
+				return status;
+			}
+
+			/* TODO: Load the CRL */
+		}
+	}
+
+	/*
+	 * Reaching here implies that the certificate and key has been loaded and
+	 * verified, now we can safely set the key size used.
+	 */
+	conn->ssl_key_bits = SecKeyGetBlockSize(key_ref);
+	return noErr;
+}
+
+/* ------------------------------------------------------------ */
+/*					SSL information functions					*/
+/* ------------------------------------------------------------ */
+
+int
+PQsslInUse(PGconn *conn)
+{
+	if (!conn)
+		return 0;
+	return conn->ssl_in_use;
+}
+
+/*
+ *	Return pointer to the Secure Transport SSL Context object.
+ */
+void *
+PQgetssl(PGconn *conn)
+{
+	if (!conn)
+		return NULL;
+	return conn->ssl;
+}
+
+void *
+PQsslStruct(PGconn *conn, const char *struct_name)
+{
+	if (!conn)
+		return NULL;
+	if (strcmp(struct_name, "SecureTransport") == 0)
+		return conn->ssl;
+	return NULL;
+}
+
+const char *const *
+PQsslAttributeNames(PGconn *conn)
+{
+	static const char *const result[] = {
+		"library",
+		"key_bits",
+		"cipher",
+		"protocol",
+		NULL
+	};
+
+	return result;
+}
+
+const char *
+PQsslAttribute(PGconn *conn, const char *attribute_name)
+{
+	SSLCipherSuite	cipher;
+	SSLProtocol		protocol;
+	OSStatus		status;
+	const char 	   *attribute = NULL;
+
+	if (!conn || !conn->ssl)
+		return NULL;
+
+	if (strcmp(attribute_name, "library") == 0)
+		attribute = "SecureTransport";
+	else if (strcmp(attribute_name, "key_bits") == 0)
+	{
+		if (conn->ssl_key_bits > 0)
+		{
+			static char sslbits_str[10];
+			snprintf(sslbits_str, sizeof(sslbits_str), "%d", conn->ssl_key_bits);
+			attribute = sslbits_str;
+		}
+	}
+	else if (strcmp(attribute_name, "cipher") == 0)
+	{
+		status = SSLGetNegotiatedCipher(conn->ssl, &cipher);
+		if (status == noErr)
+			return SSLciphername(cipher);
+	}
+	else if (strcmp(attribute_name, "protocol") == 0)
+	{
+		status = SSLGetNegotiatedProtocolVersion(conn->ssl, &protocol);
+		if (status == noErr)
+		{
+			switch (protocol)
+			{
+				case kTLSProtocol11:
+					attribute = "TLSv1.1";
+					break;
+				case kTLSProtocol12:
+					attribute = "TLSv1.2";
+					break;
+				default:
+					break;
+			}
+		}
+	}
+
+	return attribute;
+}
+
+/* ------------------------------------------------------------ */
+/*			Secure Transport Information Functions				*/
+/* ------------------------------------------------------------ */
+
+/*
+ * Obtain reason string for passed SSL errcode
+ */
+static char ssl_noerr[] = "no SSL error reported";
+static char ssl_nomem[] = "out of memory allocating error description";
+#define SSL_ERR_LEN 128
+
+static char *
+SSLerrmessage(OSStatus errcode)
+{
+	char 	   *err_buf;
+	const char *tmp;
+	CFStringRef	err_msg;
+
+	if (errcode == noErr || errcode == errSecSuccess)
+		return ssl_noerr;
+
+	err_buf = malloc(SSL_ERR_LEN);
+	if (!err_buf)
+		return ssl_nomem;
+
+	err_msg = SecCopyErrorMessageString(errcode, NULL);
+	if (err_msg)
+	{
+		tmp = CFStringGetCStringPtr(err_msg, kCFStringEncodingUTF8);
+		strlcpy(err_buf, tmp, SSL_ERR_LEN);
+		CFRelease(err_msg);
+	}
+	else
+		snprintf(err_buf, sizeof(err_buf), _("SSL error code %d"), errcode);
+
+	return err_buf;
+}
+
+static void
+SSLerrfree(char *err_buf)
+{
+	if (err_buf && err_buf != ssl_nomem && err_buf != ssl_noerr)
+		free(err_buf);
+}
+
+/*
+ * SSLsessionstate
+ *
+ * Returns 0 if the connection is open and -1 in case the connection is closed
+ * or its status unknown. If msg is non-NULL the current state is copied with
+ * at most len characters.
+ */
+static int
+SSLsessionstate(PGconn *conn, char *msg, size_t len)
+{
+	SSLSessionState		state = -1;
+	OSStatus			status = errSecInternalError;
+	const char 		   *status_msg;
+
+	/*
+	 * If conn->ssl isn't defined we will report "Unknown" which it could be
+	 * argued being correct or not, but since we don't know if there has ever
+	 * been a connection at all it's not more correct to say "Closed" or
+	 * "Aborted".
+	 */
+	if (conn->ssl)
+		status = SSLGetSessionState(conn->ssl, &state);
+
+	switch (state)
+	{
+		case kSSLConnected:
+			status_msg = "Connected";
+			status = 0;
+			break;
+		case kSSLHandshake:
+			status_msg = "Handshake";
+			status = 0;
+			break;
+		case kSSLIdle:
+			status_msg = "Idle";
+			status = 0;
+			break;
+		case kSSLClosed:
+			status_msg = "Closed";
+			status = -1;
+			break;
+		case kSSLAborted:
+			status_msg = "Aborted";
+			status = -1;
+			break;
+		default:
+			status_msg = "Unknown";
+			status = -1;
+			break;
+	}
+
+	if (msg)
+		strlcpy(msg, status_msg, len);
+
+	return (status == noErr ? 0 : -1);
+}
+
+/*
+ * SSLciphername
+ *
+ * Translate an SSLCipherSuite code into a string literal suitable for printing
+ * in log/informational messages to the user. Since this implementation of the
+ * Secure Transport lib doesn't support SSLv2/v3 these ciphernames are omitted.
+ */
+static const char *
+SSLciphername(SSLCipherSuite cipher)
+{
+	switch (cipher)
+	{
+    	case TLS_NULL_WITH_NULL_NULL:
+    		return "NULL_WITH_NULL_NULL";
+			break;
+
+		/* TLS addenda using AES, per RFC 3268 */
+		case TLS_RSA_WITH_AES_128_CBC_SHA:
+			return "RSA_WITH_AES_128_CBC_SHA";
+			break;
+		case TLS_DH_DSS_WITH_AES_128_CBC_SHA:
+			return "DH_DSS_WITH_AES_128_CBC_SHA";
+			break;
+		case TLS_DH_RSA_WITH_AES_128_CBC_SHA:
+			return "DH_RSA_WITH_AES_128_CBC_SHA";
+			break;
+		case TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
+			return "DHE_DSS_WITH_AES_128_CBC_SHA";
+			break;
+		case TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+			return "DHE_RSA_WITH_AES_128_CBC_SHA";
+			break;
+		case TLS_DH_anon_WITH_AES_128_CBC_SHA:
+			return "DH_anon_WITH_AES_128_CBC_SHA";
+			break;
+		case TLS_RSA_WITH_AES_256_CBC_SHA:
+			return "RSA_WITH_AES_256_CBC_SHA";
+			break;
+		case TLS_DH_DSS_WITH_AES_256_CBC_SHA:
+			return "DH_DSS_WITH_AES_256_CBC_SHA";
+			break;
+		case TLS_DH_RSA_WITH_AES_256_CBC_SHA:
+			return "DH_RSA_WITH_AES_256_CBC_SHA";
+			break;
+		case TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
+			return "DHE_DSS_WITH_AES_256_CBC_SHA";
+			break;
+		case TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+			return "DHE_RSA_WITH_AES_256_CBC_SHA";
+			break;
+		case TLS_DH_anon_WITH_AES_256_CBC_SHA:
+			return "DH_anon_WITH_AES_256_CBC_SHA";
+			break;
+
+		/* ECDSA addenda, RFC 4492 */
+		case TLS_ECDH_ECDSA_WITH_NULL_SHA:
+			return "ECDH_ECDSA_WITH_NULL_SHA";
+			break;
+		case TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
+			return "ECDH_ECDSA_WITH_RC4_128_SHA";
+			break;
+		case TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
+			return "ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA";
+			break;
+		case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
+			return "ECDH_ECDSA_WITH_AES_128_CBC_SHA";
+			break;
+		case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
+			return "ECDH_ECDSA_WITH_AES_256_CBC_SHA";
+			break;
+		case TLS_ECDHE_ECDSA_WITH_NULL_SHA:
+			return "ECDHE_ECDSA_WITH_NULL_SHA";
+			break;
+		case TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
+			return "ECDHE_ECDSA_WITH_RC4_128_SHA";
+			break;
+		case TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
+			return "ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA";
+			break;
+		case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
+			return "ECDHE_ECDSA_WITH_AES_128_CBC_SHA";
+			break;
+		case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+			return "ECDHE_ECDSA_WITH_AES_256_CBC_SHA";
+			break;
+		case TLS_ECDH_RSA_WITH_NULL_SHA:
+			return "ECDH_RSA_WITH_NULL_SHA";
+			break;
+		case TLS_ECDH_RSA_WITH_RC4_128_SHA:
+			return "ECDH_RSA_WITH_RC4_128_SHA";
+			break;
+		case TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
+			return "ECDH_RSA_WITH_3DES_EDE_CBC_SHA";
+			break;
+		case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
+			return "ECDH_RSA_WITH_AES_128_CBC_SHA";
+			break;
+		case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
+			return "ECDH_RSA_WITH_AES_256_CBC_SHA";
+			break;
+		case TLS_ECDHE_RSA_WITH_NULL_SHA:
+			return "ECDHE_RSA_WITH_NULL_SHA";
+			break;
+		case TLS_ECDHE_RSA_WITH_RC4_128_SHA:
+			return "ECDHE_RSA_WITH_RC4_128_SHA";
+			break;
+		case TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
+			return "ECDHE_RSA_WITH_3DES_EDE_CBC_SHA";
+			break;
+		case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+			return "ECDHE_RSA_WITH_AES_128_CBC_SHA";
+			break;
+		case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+			return "ECDHE_RSA_WITH_AES_256_CBC_SHA";
+			break;
+		case TLS_ECDH_anon_WITH_NULL_SHA:
+			return "ECDH_anon_WITH_NULL_SHA";
+			break;
+		case TLS_ECDH_anon_WITH_RC4_128_SHA:
+			return "ECDH_anon_WITH_RC4_128_SHA";
+			break;
+		case TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA:
+			return "ECDH_anon_WITH_3DES_EDE_CBC_SHA";
+			break;
+		case TLS_ECDH_anon_WITH_AES_128_CBC_SHA:
+			return "ECDH_anon_WITH_AES_128_CBC_SHA";
+			break;
+		case TLS_ECDH_anon_WITH_AES_256_CBC_SHA:
+			return "ECDH_anon_WITH_AES_256_CBC_SHA";
+			break;
+
+		/* Server provided RSA certificate for key exchange. */
+		case TLS_RSA_WITH_NULL_MD5:
+			return "RSA_WITH_NULL_MD5";
+			break;
+		case TLS_RSA_WITH_NULL_SHA:
+			return "RSA_WITH_NULL_SHA";
+			break;
+		case TLS_RSA_WITH_RC4_128_MD5:
+			return "RSA_WITH_RC4_128_MD5";
+			break;
+		case TLS_RSA_WITH_RC4_128_SHA:
+			return "RSA_WITH_RC4_128_SHA";
+			break;
+		case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+			return "RSA_WITH_3DES_EDE_CBC_SHA";
+			break;
+		case TLS_RSA_WITH_NULL_SHA256:
+			return "RSA_WITH_NULL_SHA256";
+			break;
+		case TLS_RSA_WITH_AES_128_CBC_SHA256:
+			return "RSA_WITH_AES_128_CBC_SHA256";
+			break;
+		case TLS_RSA_WITH_AES_256_CBC_SHA256:
+			return "RSA_WITH_AES_256_CBC_SHA256";
+			break;
+
+    	/*
+		 * Server-authenticated (and optionally client-authenticated)
+		 * Diffie-Hellman.
+		 */
+		case TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
+			return "DH_DSS_WITH_3DES_EDE_CBC_SHA";
+			break;
+		case TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA:
+			return "DH_RSA_WITH_3DES_EDE_CBC_SHA";
+			break;
+		case TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
+			return "DHE_DSS_WITH_3DES_EDE_CBC_SHA";
+			break;
+		case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+			return "DHE_RSA_WITH_3DES_EDE_CBC_SHA";
+			break;
+		case TLS_DH_DSS_WITH_AES_128_CBC_SHA256:
+			return "DH_DSS_WITH_AES_128_CBC_SHA256";
+			break;
+		case TLS_DH_RSA_WITH_AES_128_CBC_SHA256:
+			return "DH_RSA_WITH_AES_128_CBC_SHA256";
+			break;
+		case TLS_DHE_DSS_WITH_AES_128_CBC_SHA256:
+			return "DHE_DSS_WITH_AES_128_CBC_SHA256";
+			break;
+		case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+			return "DHE_RSA_WITH_AES_128_CBC_SHA256";
+			break;
+		case TLS_DH_DSS_WITH_AES_256_CBC_SHA256:
+			return "DH_DSS_WITH_AES_256_CBC_SHA256";
+			break;
+		case TLS_DH_RSA_WITH_AES_256_CBC_SHA256:
+			return "DH_RSA_WITH_AES_256_CBC_SHA256";
+			break;
+		case TLS_DHE_DSS_WITH_AES_256_CBC_SHA256:
+			return "DHE_DSS_WITH_AES_256_CBC_SHA256";
+			break;
+		case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+			return "DHE_RSA_WITH_AES_256_CBC_SHA256";
+			break;
+
+		/* Completely anonymous Diffie-Hellman */
+		case TLS_DH_anon_WITH_RC4_128_MD5:
+			return "DH_anon_WITH_RC4_128_MD5";
+			break;
+		case TLS_DH_anon_WITH_3DES_EDE_CBC_SHA:
+			return "DH_anon_WITH_3DES_EDE_CBC_SHA";
+			break;
+		case TLS_DH_anon_WITH_AES_128_CBC_SHA256:
+			return "DH_anon_WITH_AES_128_CBC_SHA256";
+			break;
+		case TLS_DH_anon_WITH_AES_256_CBC_SHA256:
+			return "DH_anon_WITH_AES_256_CBC_SHA256";
+			break;
+
+		/* Addendum from RFC 4279, TLS PSK */
+		case TLS_PSK_WITH_RC4_128_SHA:
+			return "PSK_WITH_RC4_128_SHA";
+			break;
+		case TLS_PSK_WITH_3DES_EDE_CBC_SHA:
+			return "PSK_WITH_3DES_EDE_CBC_SHA";
+			break;
+		case TLS_PSK_WITH_AES_128_CBC_SHA:
+			return "PSK_WITH_AES_128_CBC_SHA";
+			break;
+		case TLS_PSK_WITH_AES_256_CBC_SHA:
+			return "PSK_WITH_AES_256_CBC_SHA";
+			break;
+		case TLS_DHE_PSK_WITH_RC4_128_SHA:
+			return "DHE_PSK_WITH_RC4_128_SHA";
+			break;
+		case TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
+			return "DHE_PSK_WITH_3DES_EDE_CBC_SHA";
+			break;
+		case TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
+			return "DHE_PSK_WITH_AES_128_CBC_SHA";
+			break;
+		case TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
+			return "DHE_PSK_WITH_AES_256_CBC_SHA";
+			break;
+		case TLS_RSA_PSK_WITH_RC4_128_SHA:
+			return "RSA_PSK_WITH_RC4_128_SHA";
+			break;
+		case TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA:
+			return "RSA_PSK_WITH_3DES_EDE_CBC_SHA";
+			break;
+		case TLS_RSA_PSK_WITH_AES_128_CBC_SHA:
+			return "RSA_PSK_WITH_AES_128_CBC_SHA";
+			break;
+		case TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
+			return "RSA_PSK_WITH_AES_256_CBC_SHA";
+			break;
+
+		/* RFC 4785 - Pre-Shared Key (PSK) Ciphersuites with NULL Encryption */
+		case TLS_PSK_WITH_NULL_SHA:
+			return "PSK_WITH_NULL_SHA";
+			break;
+		case TLS_DHE_PSK_WITH_NULL_SHA:
+			return "DHE_PSK_WITH_NULL_SHA";
+			break;
+		case TLS_RSA_PSK_WITH_NULL_SHA:
+			return "RSA_PSK_WITH_NULL_SHA";
+			break;
+
+		/*
+		 * Addenda from rfc 5288 AES Galois Counter Mode (GCM) Cipher Suites
+		 * for TLS.
+		 */
+		case TLS_RSA_WITH_AES_128_GCM_SHA256:
+			return "RSA_WITH_AES_128_GCM_SHA256";
+			break;
+		case TLS_RSA_WITH_AES_256_GCM_SHA384:
+			return "RSA_WITH_AES_256_GCM_SHA384";
+			break;
+		case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
+			return "DHE_RSA_WITH_AES_128_GCM_SHA256";
+			break;
+		case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
+			return "DHE_RSA_WITH_AES_256_GCM_SHA384";
+			break;
+		case TLS_DH_RSA_WITH_AES_128_GCM_SHA256:
+			return "DH_RSA_WITH_AES_128_GCM_SHA256";
+			break;
+		case TLS_DH_RSA_WITH_AES_256_GCM_SHA384:
+			return "DH_RSA_WITH_AES_256_GCM_SHA384";
+			break;
+		case TLS_DHE_DSS_WITH_AES_128_GCM_SHA256:
+			return "DHE_DSS_WITH_AES_128_GCM_SHA256";
+			break;
+		case TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
+			return "DHE_DSS_WITH_AES_256_GCM_SHA384";
+			break;
+		case TLS_DH_DSS_WITH_AES_128_GCM_SHA256:
+			return "DH_DSS_WITH_AES_128_GCM_SHA256";
+			break;
+		case TLS_DH_DSS_WITH_AES_256_GCM_SHA384:
+			return "DH_DSS_WITH_AES_256_GCM_SHA384";
+			break;
+		case TLS_DH_anon_WITH_AES_128_GCM_SHA256:
+			return "DH_anon_WITH_AES_128_GCM_SHA256";
+			break;
+		case TLS_DH_anon_WITH_AES_256_GCM_SHA384:
+			return "DH_anon_WITH_AES_256_GCM_SHA384";
+			break;
+
+		/* RFC 5487 - PSK with SHA-256/384 and AES GCM */
+		case TLS_PSK_WITH_AES_128_GCM_SHA256:
+			return "PSK_WITH_AES_128_GCM_SHA256";
+			break;
+		case TLS_PSK_WITH_AES_256_GCM_SHA384:
+			return "PSK_WITH_AES_256_GCM_SHA384";
+			break;
+		case TLS_DHE_PSK_WITH_AES_128_GCM_SHA256:
+			return "DHE_PSK_WITH_AES_128_GCM_SHA256";
+			break;
+		case TLS_DHE_PSK_WITH_AES_256_GCM_SHA384:
+			return "DHE_PSK_WITH_AES_256_GCM_SHA384";
+			break;
+		case TLS_RSA_PSK_WITH_AES_128_GCM_SHA256:
+			return "RSA_PSK_WITH_AES_128_GCM_SHA256";
+			break;
+		case TLS_RSA_PSK_WITH_AES_256_GCM_SHA384:
+			return "RSA_PSK_WITH_AES_256_GCM_SHA384";
+			break;
+		case TLS_PSK_WITH_AES_128_CBC_SHA256:
+			return "PSK_WITH_AES_128_CBC_SHA256";
+			break;
+		case TLS_PSK_WITH_AES_256_CBC_SHA384:
+			return "PSK_WITH_AES_256_CBC_SHA384";
+			break;
+		case TLS_PSK_WITH_NULL_SHA256:
+			return "PSK_WITH_NULL_SHA256";
+			break;
+		case TLS_PSK_WITH_NULL_SHA384:
+			return "PSK_WITH_NULL_SHA384";
+			break;
+		case TLS_DHE_PSK_WITH_AES_128_CBC_SHA256:
+			return "DHE_PSK_WITH_AES_128_CBC_SHA256";
+			break;
+		case TLS_DHE_PSK_WITH_AES_256_CBC_SHA384:
+			return "DHE_PSK_WITH_AES_256_CBC_SHA384";
+			break;
+		case TLS_DHE_PSK_WITH_NULL_SHA256:
+			return "DHE_PSK_WITH_NULL_SHA256";
+			break;
+		case TLS_DHE_PSK_WITH_NULL_SHA384:
+			return "DHE_PSK_WITH_NULL_SHA384";
+			break;
+		case TLS_RSA_PSK_WITH_AES_128_CBC_SHA256:
+			return "RSA_PSK_WITH_AES_128_CBC_SHA256";
+			break;
+		case TLS_RSA_PSK_WITH_AES_256_CBC_SHA384:
+			return "RSA_PSK_WITH_AES_256_CBC_SHA384";
+			break;
+		case TLS_RSA_PSK_WITH_NULL_SHA256:
+			return "RSA_PSK_WITH_NULL_SHA256";
+			break;
+		case TLS_RSA_PSK_WITH_NULL_SHA384:
+			return "RSA_PSK_WITH_NULL_SHA384";
+			break;
+
+		/*
+		 * Addenda from rfc 5289  Elliptic Curve Cipher Suites with
+		 * HMAC SHA-256/384.
+		 */
+		case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+			return "ECDHE_ECDSA_WITH_AES_128_CBC_SHA256";
+			break;
+		case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
+			return "ECDHE_ECDSA_WITH_AES_256_CBC_SHA384";
+			break;
+		case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
+			return "ECDH_ECDSA_WITH_AES_128_CBC_SHA256";
+			break;
+		case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
+			return "ECDH_ECDSA_WITH_AES_256_CBC_SHA384";
+			break;
+		case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+			return "ECDHE_RSA_WITH_AES_128_CBC_SHA256";
+			break;
+		case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
+			return "ECDHE_RSA_WITH_AES_256_CBC_SHA384";
+			break;
+		case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
+			return "ECDH_RSA_WITH_AES_128_CBC_SHA256";
+			break;
+		case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
+			return "ECDH_RSA_WITH_AES_256_CBC_SHA384";
+			break;
+
+		/*
+		 * Addenda from rfc 5289  Elliptic Curve Cipher Suites with
+		 * SHA-256/384 and AES Galois Counter Mode (GCM)
+		 */
+		case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+			return "ECDHE_ECDSA_WITH_AES_128_GCM_SHA256";
+			break;
+		case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+			return "ECDHE_ECDSA_WITH_AES_256_GCM_SHA384";
+			break;
+		case TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
+			return "ECDH_ECDSA_WITH_AES_128_GCM_SHA256";
+			break;
+		case TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384:
+			return "ECDH_ECDSA_WITH_AES_256_GCM_SHA384";
+			break;
+		case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+			return "ECDHE_RSA_WITH_AES_128_GCM_SHA256";
+			break;
+		case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+			return "ECDHE_RSA_WITH_AES_256_GCM_SHA384";
+			break;
+		case TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
+			return "ECDH_RSA_WITH_AES_128_GCM_SHA256";
+			break;
+		case TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
+			return "ECDH_RSA_WITH_AES_256_GCM_SHA384";
+			break;
+
+		default:
+			break;
+	}
+
+	return NULL;
+}
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index be6c370..649d22f 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -82,6 +82,15 @@ typedef struct
 #endif
 #endif   /* USE_OPENSSL */
 
+#ifdef USE_SECURETRANSPORT
+#define Size pg_Size
+#define uint64 pg_uint64
+#include <Security/Security.h>
+#include <CoreFoundation/CoreFoundation.h>
+#undef Size
+#undef uint64
+#endif
+
 /*
  * POSTGRES backend dependent Constants.
  */
@@ -439,6 +448,14 @@ struct pg_conn
 								 * OpenSSL version changes */
 #endif
 #endif   /* USE_OPENSSL */
+
+#ifdef USE_SECURETRANSPORT
+	SSLContextRef	 ssl;		/* SSL context reference */
+	CFArrayRef		 st_rootcert;
+	ssize_t			 ssl_buffered;
+	int				 ssl_key_bits;
+#endif   /* USE_SECURETRANSPORT */
+
 #endif   /* USE_SSL */
 
 #ifdef ENABLE_GSS
-- 
2.6.4 (Apple Git-63)

0002-Enable-ssl-tests-to-be-using-a-different-set-of-Post.patchapplication/octet-stream; name=0002-Enable-ssl-tests-to-be-using-a-different-set-of-Post.patchDownload
From d7038d757d66189febd0d03a3c4480acfa1b40b2 Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <daniel@yesql.se>
Date: Tue, 4 Oct 2016 14:43:39 +0200
Subject: [PATCH 2/3] Enable ssl tests to be using a different set of
 PostgreSQL binaries

When implementing support for a new SSL library it is convenient to
be able to use a different set of server binaries which can use an
existing backend SSL implementation. Add support for bringing up a
PostgresNode with a non-local set of binaries and introduce an ENV
variable SSLTEST_SERVER_BIN which if set overrides the PATH for the
postgres binaries to use.
---
 src/test/perl/PostgresNode.pm  | 70 +++++++++++++++++++++++++++++++++---------
 src/test/ssl/t/001_ssltests.pl |  2 +-
 2 files changed, 56 insertions(+), 16 deletions(-)

diff --git a/src/test/perl/PostgresNode.pm b/src/test/perl/PostgresNode.pm
index 4b225e1..d5812a8 100644
--- a/src/test/perl/PostgresNode.pm
+++ b/src/test/perl/PostgresNode.pm
@@ -122,7 +122,7 @@ INIT
 
 =over
 
-=item PostgresNode::new($class, $name, $pghost, $pgport)
+=item PostgresNode::new($class, $name, $pghost, $pgport, $pgpath)
 
 Create a new PostgresNode instance. Does not initdb or start it.
 
@@ -133,12 +133,14 @@ of finding port numbers, registering instances for cleanup, etc.
 
 sub new
 {
-	my ($class, $name, $pghost, $pgport) = @_;
+	my ($class, $name, $pghost, $pgport, $pgpath) = @_;
 	my $testname = basename($0);
 	$testname =~ s/\.[^.]+$//;
+	$pgpath = '' unless defined $pgpath;
 	my $self = {
 		_port    => $pgport,
 		_host    => $pghost,
+		_pgpath  => $pgpath,
 		_basedir => TestLib::tempdir("data_" . $name),
 		_name    => $name,
 		_logfile => "$TestLib::log_path/${testname}_${name}.log" };
@@ -213,6 +215,21 @@ sub name
 
 =pod
 
+=item $node->pgpath()
+
+Path to the PostgreSQL binaries to run, if empty then the installation in PATH
+is assumed.
+
+=cut
+
+sub pgpath
+{
+	my ($self) = @_;
+	return $self->{_pgpath};
+}
+
+=pod
+
 =item $node->logfile()
 
 Path to the PostgreSQL log file for this instance.
@@ -313,6 +330,7 @@ sub info
 	my $_info = '';
 	open my $fh, '>', \$_info or die;
 	print $fh "Name: " . $self->name . "\n";
+	print $fh "Binary directory: " . $self->pgpath . "\n";
 	print $fh "Data directory: " . $self->data_dir . "\n";
 	print $fh "Backup directory: " . $self->backup_dir . "\n";
 	print $fh "Archive directory: " . $self->archive_dir . "\n";
@@ -393,6 +411,7 @@ sub init
 	my $port   = $self->port;
 	my $pgdata = $self->data_dir;
 	my $host   = $self->host;
+	my $pgpath = $self->pgpath;
 
 	$params{hba_permit_replication} = 1
 	  unless defined $params{hba_permit_replication};
@@ -402,7 +421,7 @@ sub init
 	mkdir $self->backup_dir;
 	mkdir $self->archive_dir;
 
-	TestLib::system_or_bail('initdb', '-D', $pgdata, '-A', 'trust', '-N',
+	TestLib::system_or_bail($pgpath . 'initdb', '-D', $pgdata, '-A', 'trust', '-N',
 		@{ $params{extra} });
 	TestLib::system_or_bail($ENV{PG_REGRESS}, '--config-auth', $pgdata);
 
@@ -481,9 +500,10 @@ sub backup
 	my $backup_path = $self->backup_dir . '/' . $backup_name;
 	my $port        = $self->port;
 	my $name        = $self->name;
+	my $pgpath      = $self->pgpath;
 
 	print "# Taking pg_basebackup $backup_name from node \"$name\"\n";
-	TestLib::system_or_bail('pg_basebackup', '-D', $backup_path, '-p', $port,
+	TestLib::system_or_bail($pgpath . 'pg_basebackup', '-D', $backup_path, '-p', $port,
 		'-x', '--nosync');
 	print "# Backup finished\n";
 }
@@ -644,8 +664,9 @@ sub start
 	my $port   = $self->port;
 	my $pgdata = $self->data_dir;
 	my $name   = $self->name;
+	my $pgpath = $self->pgpath;
 	print("### Starting node \"$name\"\n");
-	my $ret = TestLib::system_log('pg_ctl', '-w', '-D', $self->data_dir, '-l',
+	my $ret = TestLib::system_log($pgpath . 'pg_ctl', '-w', '-D', $self->data_dir, '-l',
 		$self->logfile, 'start');
 
 	if ($ret != 0)
@@ -672,10 +693,11 @@ sub stop
 	my $port   = $self->port;
 	my $pgdata = $self->data_dir;
 	my $name   = $self->name;
+	my $pgpath = $self->pgpath;
 	$mode = 'fast' unless defined $mode;
 	return unless defined $self->{_pid};
 	print "### Stopping node \"$name\" using mode $mode\n";
-	TestLib::system_log('pg_ctl', '-D', $pgdata, '-m', $mode, 'stop');
+	TestLib::system_log($pgpath . 'pg_ctl', '-D', $pgdata, '-m', $mode, 'stop');
 	$self->{_pid} = undef;
 	$self->_update_pid;
 }
@@ -694,8 +716,9 @@ sub reload
 	my $port   = $self->port;
 	my $pgdata = $self->data_dir;
 	my $name   = $self->name;
+	my $pgpath = $self->pgpath;
 	print "### Reloading node \"$name\"\n";
-	TestLib::system_log('pg_ctl', '-D', $pgdata, 'reload');
+	TestLib::system_log($pgpath . 'pg_ctl', '-D', $pgdata, 'reload');
 }
 
 =pod
@@ -713,8 +736,9 @@ sub restart
 	my $pgdata  = $self->data_dir;
 	my $logfile = $self->logfile;
 	my $name    = $self->name;
+	my $pgpath  = $self->pgpath;
 	print "### Restarting node \"$name\"\n";
-	TestLib::system_log('pg_ctl', '-D', $pgdata, '-w', '-l', $logfile,
+	TestLib::system_log($pgpath . 'pg_ctl', '-D', $pgdata, '-w', '-l', $logfile,
 		'restart');
 	$self->_update_pid;
 }
@@ -734,8 +758,9 @@ sub promote
 	my $pgdata  = $self->data_dir;
 	my $logfile = $self->logfile;
 	my $name    = $self->name;
+	my $pgpath  = $self->pgpath;
 	print "### Promoting node \"$name\"\n";
-	TestLib::system_log('pg_ctl', '-D', $pgdata, '-l', $logfile, 'promote');
+	TestLib::system_log($pgpath . 'pg_ctl', '-D', $pgdata, '-l', $logfile, 'promote');
 }
 
 # Internal routine to enable streaming replication on a standby node.
@@ -833,10 +858,12 @@ sub _update_pid
 
 =pod
 
-=item get_new_node(node_name)
+=item get_new_node(node_name, pgpath)
 
 Build a new PostgresNode object, assigning a free port number. Standalone
-function that's automatically imported.
+function that's automatically imported. If the pgpath parameter is defined then
+the path is tested for containing a PostgreSQL installation with the correct
+permissions for execution.
 
 Remembers the node, to prevent its port number from being reused for another
 node, and to ensure that it gets shut down when the test script exits.
@@ -847,7 +874,7 @@ You should generally use this instead of PostgresNode::new(...).
 
 sub get_new_node
 {
-	my $name  = shift;
+	my ($name, $pgpath) = @_;
 	my $found = 0;
 	my $port  = $last_port_assigned;
 
@@ -890,8 +917,19 @@ sub get_new_node
 
 	print "# Found free port $port\n";
 
+	# If a separate path to the binaries was specified then we do rudimentary
+	# checks for a sane installation. If an executable copy of initdb is there
+	# then let's go ahead.
+	if (defined $pgpath)
+	{
+		$pgpath =~ s!/*$!/!;
+		die "initdb missing in $pgpath" unless -x $pgpath . 'initdb';
+
+		print "# Found PostgreSQL installation in $pgpath\n";
+	}
+
 	# Lock port number found by creating a new node
-	my $node = new PostgresNode($name, $test_pghost, $port);
+	my $node = new PostgresNode($name, $test_pghost, $port, $pgpath);
 
 	# Add node to list of nodes
 	push(@all_nodes, $node);
@@ -1060,8 +1098,9 @@ sub psql
 	my $stderr            = $params{stderr};
 	my $timeout           = undef;
 	my $timeout_exception = 'psql timed out';
+	my $pgpath            = $self->pgpath;
 	my @psql_params =
-	  ('psql', '-XAtq', '-d', $self->connstr($dbname), '-f', '-');
+	  ($pgpath . 'psql', '-XAtq', '-d', $self->connstr($dbname), '-f', '-');
 
 	# If the caller wants an array and hasn't passed stdout/stderr
 	# references, allocate temporary ones to capture them so we
@@ -1202,12 +1241,13 @@ sub poll_query_until
 
 	my $max_attempts = 180;
 	my $attempts     = 0;
+	my $pgpath       = $self->pgpath();
 	my ($stdout, $stderr);
 
 	while ($attempts < $max_attempts)
 	{
 		my $cmd =
-		  [ 'psql', '-XAt', '-c', $query, '-d', $self->connstr($dbname) ];
+		  [ $pgpath . 'psql', '-XAt', '-c', $query, '-d', $self->connstr($dbname) ];
 		my $result = IPC::Run::run $cmd, '>', \$stdout, '2>', \$stderr;
 
 		chomp($stdout);
diff --git a/src/test/ssl/t/001_ssltests.pl b/src/test/ssl/t/001_ssltests.pl
index 80e8ea1..f42c5c1 100644
--- a/src/test/ssl/t/001_ssltests.pl
+++ b/src/test/ssl/t/001_ssltests.pl
@@ -74,7 +74,7 @@ chmod 0600, "ssl/client.key";
 #### Part 0. Set up the server.
 
 diag "setting up data directory...";
-my $node = get_new_node('master');
+my $node = get_new_node('master', $ENV{SSLTEST_SERVER_BIN});
 $node->init;
 
 # PGHOST is enforced here to set up the node, subsequent connections
-- 
2.6.4 (Apple Git-63)

0003-A-first-stab-at-updating-the-docs-to-handle-multiple.patchapplication/octet-stream; name=0003-A-first-stab-at-updating-the-docs-to-handle-multiple.patchDownload
From 4e15bdbec554609f0ed441bfc680479b8c6a04c1 Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <daniel@yesql.se>
Date: Tue, 4 Oct 2016 14:43:43 +0200
Subject: [PATCH 3/3] A first stab at updating the docs to handle multiple SSL
 libraries

At this point it's a mere start and a nudge in the corner and not
even close to a finished patch. Keeping the current structure of
having a single set of config option seems key though, keeping the
differences between the libraries small and contained.
---
 doc/src/sgml/libpq.sgml   |  44 +++++++++++-----
 doc/src/sgml/runtime.sgml | 124 +++++++++++++++++++++++++++++++++-------------
 2 files changed, 121 insertions(+), 47 deletions(-)

diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 4e34f00..78fcf4c 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1237,13 +1237,14 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
       <term><literal>sslcompression</literal></term>
       <listitem>
        <para>
+        This requires <productname>OpenSSL</> 1.0.0 or later, when using
+        other SSL libraries this setting will be ignored.
         If set to 1 (default), data sent over SSL connections will be
         compressed.
-        If set to 0, compression will be disabled (this requires
-        <productname>OpenSSL</> 1.0.0 or later).
+        If set to 0, compression will be disabled.
         This parameter is ignored if a connection without SSL is made,
-        or if the version of <productname>OpenSSL</> used does not support
-        it.
+        if the version of <productname>OpenSSL</> used does not support
+        it or an SSL library other than <productname>OpenSSL</> is used.
        </para>
        <para>
         Compression uses CPU time, but can improve throughput if
@@ -1304,7 +1305,9 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
         revocation list (CRL).  Certificates listed in this file, if it
         exists, will be rejected while attempting to authenticate the
         server's certificate.  The default is
-        <filename>~/.postgresql/root.crl</>.
+        <filename>~/.postgresql/root.crl</>. This setting is ignored
+        when using <productname>Secure Transport</> which require the
+        CRL to be included in the certificate.
        </para>
       </listitem>
      </varlistentry>
@@ -1900,8 +1903,7 @@ const char *PQsslAttribute(const PGconn *conn, const char *attribute_name);
          <term><literal>library</literal></term>
           <listitem>
            <para>
-            Name of the SSL implementation in use. (Currently, only
-            <literal>"OpenSSL"</literal> is implemented)
+            Name of the SSL implementation in use.
            </para>
           </listitem>
          </varlistentry>
@@ -7322,9 +7324,14 @@ ldap://ldap.acme.com/cn=dbserver,cn=hosts?pgconnectinfo?base?(objectclass=*)
    <productname>PostgreSQL</> has native support for using <acronym>SSL</>
    connections to encrypt client/server communications for increased
    security. See <xref linkend="ssl-tcp"> for details about the server-side
-   <acronym>SSL</> functionality.
+   <acronym>SSL</> functionality. Multiple SSL libraries are supported and
+   are enabled at build time.
+   Table <xref linkend="supported-ssl-libraries"> lists the supported SSL
+   libraries.
   </para>
 
+  <sect2>
+   <title>Using OpenSSL</title>
   <para>
    <application>libpq</application> reads the system-wide
    <productname>OpenSSL</productname> configuration file. By default, this
@@ -7334,6 +7341,15 @@ ldap://ldap.acme.com/cn=dbserver,cn=hosts?pgconnectinfo?base?(objectclass=*)
    <envar>OPENSSL_CONF</envar> to the name of the desired configuration
    file.
   </para>
+  </sect2>
+
+  <sect2>
+   <title>Using Secure Transport</title>
+  <para>
+   <productname>Secure Transport</productname> utilize the Keychain of the
+   current user. WRITEME. 
+  </para>
+  </sect2>
 
  <sect2 id="libq-ssl-certificates">
   <title>Client Verification of Server Certificates</title>
@@ -7380,10 +7396,13 @@ ldap://ldap.acme.com/cn=dbserver,cn=hosts?pgconnectinfo?base?(objectclass=*)
   </para>
 
   <para>
-   Certificate Revocation List (CRL) entries are also checked
-   if the file <filename>~/.postgresql/root.crl</filename> exists
+   Certificate Revocation List (<acronym>CRL</>) entries are also checked. When libpq
+   is built using <productname>OpenSSL</>
+   the file <filename>~/.postgresql/root.crl</filename> is used
    (<filename>%APPDATA%\postgresql\root.crl</filename> on Microsoft
-   Windows).
+   Windows). When using <productname>Secure Transport</> the CRL must be
+   included in the server certificate for Keychain to perform certificate
+   revocation checks automatically.
   </para>
 
   <para>
@@ -7661,7 +7680,8 @@ ldap://ldap.acme.com/cn=dbserver,cn=hosts?pgconnectinfo?base?(objectclass=*)
      <row>
       <entry><filename>~/.postgresql/root.crl</></entry>
       <entry>certificates revoked by certificate authorities</entry>
-      <entry>server certificate must not be on this list</entry>
+      <entry>any certificate in this list will be rejected; server certificate
+      must not be on this list</entry>
      </row>
 
     </tbody>
diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml
index 88ec120..632939c 100644
--- a/doc/src/sgml/runtime.sgml
+++ b/doc/src/sgml/runtime.sgml
@@ -2117,12 +2117,42 @@ pg_dumpall -p 5432 | psql -d postgres -p 5433
   <para>
    <productname>PostgreSQL</> has native support for using
    <acronym>SSL</> connections to encrypt client/server communications
-   for increased security. This requires that
-   <productname>OpenSSL</productname> is installed on both client and
+   for increased security. This requires that a supported SSL library
+   is installed on both client and
    server systems and that support in <productname>PostgreSQL</> is
-   enabled at build time (see <xref linkend="installation">).
+   enabled at build time (see <xref linkend="installation">). Clients are
+   not required to use the same SSL library as the server, all supported
+   libraries are compatible.
+   Table <xref linkend="supported-ssl-libraries"> lists the supported SSL
+   libraries.
   </para>
 
+  <table id="supported-ssl-libraries">
+   <title>Supported SSL libraries</title>
+   <tgroup cols="2">
+    <thead>
+     <row>
+      <entry>Library</entry>
+      <entry>Platform</entry>
+     </row>
+    </thead>
+
+    <tbody>
+
+     <row>
+      <entry><productname>OpenSSL</productname></entry>
+      <entry>All platforms supported by <productname>PostgreSQL</productname></entry>
+     </row>
+
+     <row>
+      <entry><productname>Secure Transport</productname></entry>
+      <entry><productname>Apple macOS</productname></entry>
+     </row>
+
+    </tbody>
+   </tgroup>
+  </table>
+
   <para>
    With <acronym>SSL</> support compiled in, the
    <productname>PostgreSQL</> server can be started with
@@ -2137,41 +2167,13 @@ pg_dumpall -p 5432 | psql -d postgres -p 5433
   </para>
 
   <para>
-   <productname>PostgreSQL</productname> reads the system-wide
-   <productname>OpenSSL</productname> configuration file. By default, this
-   file is named <filename>openssl.cnf</filename> and is located in the
-   directory reported by <literal>openssl version -d</>.
-   This default can be overridden by setting environment variable
-   <envar>OPENSSL_CONF</envar> to the name of the desired configuration file.
-  </para>
-
-  <para>
-   <productname>OpenSSL</productname> supports a wide range of ciphers
-   and authentication algorithms, of varying strength.  While a list of
-   ciphers can be specified in the <productname>OpenSSL</productname>
-   configuration file, you can specify ciphers specifically for use by
-   the database server by modifying <xref linkend="guc-ssl-ciphers"> in
-   <filename>postgresql.conf</>.
-  </para>
-
-  <note>
-   <para>
-    It is possible to have authentication without encryption overhead by
-    using <literal>NULL-SHA</> or <literal>NULL-MD5</> ciphers.  However,
-    a man-in-the-middle could read and pass communications between client
-    and server.  Also, encryption overhead is minimal compared to the
-    overhead of authentication.  For these reasons NULL ciphers are not
-    recommended.
-   </para>
-  </note>
-
-  <para>
-   To start in <acronym>SSL</> mode, files containing the server certificate
-   and private key must exist.  By default, these files are expected to be
+   To start in <acronym>SSL</> mode, a server certificate
+   and private key must exist.  By default, these are expected to be in files
    named <filename>server.crt</> and <filename>server.key</>, respectively, in
    the server's data directory, but other names and locations can be specified
    using the configuration parameters <xref linkend="guc-ssl-cert-file">
-   and <xref linkend="guc-ssl-key-file">.
+   and <xref linkend="guc-ssl-key-file">. See the notes on the library used
+   for specifics on how certificates and keys can be specified.
   </para>
 
   <para>
@@ -2202,6 +2204,58 @@ pg_dumpall -p 5432 | psql -d postgres -p 5433
    <filename>root.crt</filename> files.
   </para>
 
+  <note>
+   <para>
+    It is possible to have authentication without encryption overhead by
+    using NULL ciphers (<literal>NULL-SHA</> or <literal>NULL-MD5</> in
+    <productname>OpenSSL</productname> for example).  However,
+    a man-in-the-middle could read and pass communications between client
+    and server.  Also, encryption overhead is minimal compared to the
+    overhead of authentication.  For these reasons NULL ciphers are not
+    recommended.
+   </para>
+  </note>
+
+  <sect2 id="ssl-library-openssl">
+   <title>Using OpenSSL</title>
+
+  <para>
+   <productname>PostgreSQL</productname> reads the system-wide
+   <productname>OpenSSL</productname> configuration file. By default, this
+   file is named <filename>openssl.cnf</filename> and is located in the
+   directory reported by <literal>openssl version -d</>.
+   This default can be overridden by setting environment variable
+   <envar>OPENSSL_CONF</envar> to the name of the desired configuration file.
+  </para>
+
+  <para>
+   <productname>OpenSSL</productname> supports a wide range of ciphers
+   and authentication algorithms, of varying strength.  While a list of
+   ciphers can be specified in the <productname>OpenSSL</productname>
+   configuration file, you can specify ciphers specifically for use by
+   the database server by modifying <xref linkend="guc-ssl-ciphers"> in
+   <filename>postgresql.conf</>.
+  </para>
+  </sect2>
+
+  <sect2 id="ssl-library-secure-transport">
+   <title>Using Secure Transport</title>
+   
+  <para>
+   <productname>Secure Transport</productname> can use certificates and
+   keys either stored in files or load them from
+   <productname>Keychain</productname> by prefixing the configuration
+   parameters <xref linkend="guc-ssl-cert-file"> and
+   <xref linkend="guc-ssl-key-file"> with <literal>keychain:</>.
+   It is not possible to load certificate revocation list, <acronym>CRL</acronym>,
+   manually with <productname>Secure Transport</productname>, certificate revocation
+   can only be handled from within <productname>Keychain</productname>.  When using a
+   certificate stored in <productname>Keychain</productname>, certificate revocation
+   is automatically supported via CRL and <acronym>OCSP</acronym> if the URL for
+   the revocation responder is contained in the certificate.
+  </para>
+  </sect2>
+  
   <sect2 id="ssl-client-certificates">
    <title>Using Client Certificates</title>
 
-- 
2.6.4 (Apple Git-63)

#2Stephen Frost
sfrost@snowman.net
In reply to: Daniel Gustafsson (#1)
Re: WIP: Secure Transport support as OpenSSL alternative on macOS

Daniel,

* Daniel Gustafsson (daniel@yesql.se) wrote:

The main questions raised here are: is it of interest to support multiple SSL
libraries given the additional support burden and; is supporting Secure
Transport of any interest or is it a wasted effort to continue towards a full
frontend/backend/doc submission?

We've already started working towards being able to support multiple
libraries (in particular, Heikki's work to try and contain the OpenSSL
bits) and I've been looking at what it'll take to add NSS support.

Generally speaking, I think we'd be better off looking at adding support
for multi-platform libraries like NSS rather than OS-specific APIs, but
I do understand how there can be advantages to using the OS API
(keychain integration is certainly a big part of that, as you mention;
the same is true for our Kerberos/GSS support).

On top of the OpenSSL situation in macOS, supporting Secure Transport makes
sense if the Keychain can be fully leveraged since that would allow IT
organisations to centrally manage and provision Mac clients without passing
certificate/key files around. Should there be any interest in this I intend to
continue on one more library which is more geared towards server environments
like nss or <suggestions-welcome> once Secure Transport is done (scratching
another itch).

I'd certainly be more than happy to help with the NSS side of things,
though I'd want to have both client and server support for it.

The attached show-and-tell patches are:

Haven't actually looked at the patches yet, but very happy that you're
also interested in this. :)

0003. A first little stab at tweaking the docs to mention support for
multiple SSL libraries. There is a lot left here but the idea is to contain
the library specifics in subsections with the main sections being generic
SSL support information. On top of tweaking the existing sections I intend
(once done) to write up the steps needed to implement support for an SSL
library, this should perhaps be a README in the tree though?

Agreed, this would be better as a README rather than in the formal docs,
since adding support for another SSL library isn't really user-facing.

Thanks!

Stephen

#3Daniel Gustafsson
daniel@yesql.se
In reply to: Stephen Frost (#2)
Re: WIP: Secure Transport support as OpenSSL alternative on macOS

On 05 Oct 2016, at 16:45, Stephen Frost <sfrost@snowman.net> wrote:

Daniel,

* Daniel Gustafsson (daniel@yesql.se) wrote:

The main questions raised here are: is it of interest to support multiple SSL
libraries given the additional support burden and; is supporting Secure
Transport of any interest or is it a wasted effort to continue towards a full
frontend/backend/doc submission?

We've already started working towards being able to support multiple
libraries (in particular, Heikki's work to try and contain the OpenSSL
bits) and I've been looking at what it'll take to add NSS support.

Building on his work it’s quite simple to keep this code contained to its own
files which is great.

Generally speaking, I think we'd be better off looking at adding support
for multi-platform libraries like NSS rather than OS-specific APIs, but
I do understand how there can be advantages to using the OS API
(keychain integration is certainly a big part of that, as you mention;
the same is true for our Kerberos/GSS support).

In general I agree with you, supporting OS specific APIs on common platforms
(for some value of) which doesn’t ship with a useable OpenSSL installation can
still be of value I think.

On top of the OpenSSL situation in macOS, supporting Secure Transport makes
sense if the Keychain can be fully leveraged since that would allow IT
organisations to centrally manage and provision Mac clients without passing
certificate/key files around. Should there be any interest in this I intend to
continue on one more library which is more geared towards server environments
like nss or <suggestions-welcome> once Secure Transport is done (scratching
another itch).

I'd certainly be more than happy to help with the NSS side of things,
though I'd want to have both client and server support for it.

I think any library supported should be required to have both client and server
support. I stopped short after the frontend in this patch to get feedback but
will absolutely implement the backend as well.

NSS is on my list and I intend to get started on that somewhat shortly.

The attached show-and-tell patches are:

Haven't actually looked at the patches yet, but very happy that you're
also interested in this. :)

0003. A first little stab at tweaking the docs to mention support for
multiple SSL libraries. There is a lot left here but the idea is to contain
the library specifics in subsections with the main sections being generic
SSL support information. On top of tweaking the existing sections I intend
(once done) to write up the steps needed to implement support for an SSL
library, this should perhaps be a README in the tree though?

Agreed, this would be better as a README rather than in the formal docs,
since adding support for another SSL library isn't really user-facing.

cheers ./daniel

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#4Robert Haas
robertmhaas@gmail.com
In reply to: Daniel Gustafsson (#1)
Re: WIP: Secure Transport support as OpenSSL alternative on macOS

On Wed, Oct 5, 2016 at 5:36 AM, Daniel Gustafsson <daniel@yesql.se> wrote:

The main questions raised here are: is it of interest to support multiple SSL
libraries given the additional support burden and; is supporting Secure
Transport of any interest or is it a wasted effort to continue towards a full
frontend/backend/doc submission?

I think this is highly worthwhile. I wish we could interest someone
in doing the work for Windows ... but I'm a macOS user myself, so I'll
be happy to have you fix my future compile problems for me.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#5Tom Lane
tgl@sss.pgh.pa.us
In reply to: Robert Haas (#4)
Re: WIP: Secure Transport support as OpenSSL alternative on macOS

Robert Haas <robertmhaas@gmail.com> writes:

On Wed, Oct 5, 2016 at 5:36 AM, Daniel Gustafsson <daniel@yesql.se> wrote:

The main questions raised here are: is it of interest to support multiple SSL
libraries given the additional support burden and; is supporting Secure
Transport of any interest or is it a wasted effort to continue towards a full
frontend/backend/doc submission?

I think this is highly worthwhile. I wish we could interest someone
in doing the work for Windows ... but I'm a macOS user myself, so I'll
be happy to have you fix my future compile problems for me.

"Future"? Apple isn't even shipping the OpenSSL headers anymore, and
I imagine soon no libraries either. We really have got little choice
on that platform but to do something with Secure Transport. I'm glad
somebody is taking up the task.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#6Magnus Hagander
magnus@hagander.net
In reply to: Tom Lane (#5)
Re: WIP: Secure Transport support as OpenSSL alternative on macOS

On Wed, Oct 5, 2016 at 8:42 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Robert Haas <robertmhaas@gmail.com> writes:

On Wed, Oct 5, 2016 at 5:36 AM, Daniel Gustafsson <daniel@yesql.se>

wrote:

The main questions raised here are: is it of interest to support

multiple SSL

libraries given the additional support burden and; is supporting Secure
Transport of any interest or is it a wasted effort to continue towards

a full

frontend/backend/doc submission?

I think this is highly worthwhile. I wish we could interest someone
in doing the work for Windows ... but I'm a macOS user myself, so I'll
be happy to have you fix my future compile problems for me.

"Future"? Apple isn't even shipping the OpenSSL headers anymore, and
I imagine soon no libraries either. We really have got little choice
on that platform but to do something with Secure Transport. I'm glad
somebody is taking up the task.

Sure we do. Windows doesn't ship them either, and yet somehow Postgres
manages to work just fine there, including with openssl support. There's
nothing more magic about MacOS than there is for Windows.

That said, I agree that somebody is picking up the task. I actually think
it would be a lot more useful to get Windows SChannel support (because it's
*much* more of a PITA to get OpenSSL onto Windows than it is to get it onto
macOS) or even moreso NSS (because then every platform could use that, and
they have other integrations too). But one important point is that once we
have *two* implementations (openssl + macos) then we will know a lot more
about the correct places for abstractions etc, and and adding the third one
is probably going to be easier (but not easy). But let's make sure we keep
in mind there should be more than just these two implementation when
defining any external interfaces.

--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/