>From 5192f9cfe8efe322f6b2d5c1e70290a250f9d0c3 Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <daniel@yesql.se>
Date: Wed, 2 Aug 2017 23:09:07 +0200
Subject: [PATCH 2/2] WIP: Add support for Apple Secure Transport SSL library

This adds frontend and backend support for the Secure Transport SSL
library on macOS. The patch is not in a final state, but works to a
fairly large degree. See email to -hackers for full details.
---
 configure                                        |   57 +
 configure.in                                     |   14 +
 src/Makefile.global.in                           |    1 +
 src/backend/Makefile                             |    4 +
 src/backend/libpq/Makefile                       |    5 +
 src/backend/libpq/be-secure-securetransport.c    | 1028 +++++++++++++++++
 src/backend/utils/misc/guc.c                     |   13 +
 src/include/common/securetransport.h             |  510 ++++++++
 src/include/common/sha2.h                        |    4 +-
 src/include/libpq/libpq-be.h                     |   17 +-
 src/include/pg_config.h.in                       |    3 +
 src/include/pg_config_manual.h                   |   10 +-
 src/interfaces/libpq/Makefile                    |    6 +-
 src/interfaces/libpq/fe-connect.c                |    6 +
 src/interfaces/libpq/fe-secure-securetransport.c | 1342 ++++++++++++++++++++++
 src/interfaces/libpq/libpq-int.h                 |   22 +-
 16 files changed, 3031 insertions(+), 11 deletions(-)
 create mode 100644 src/backend/libpq/be-secure-securetransport.c
 create mode 100644 src/include/common/securetransport.h
 create mode 100644 src/interfaces/libpq/fe-secure-securetransport.c

diff --git a/configure b/configure
index 8b24bf1128..a345d4b2bd 100755
--- a/configure
+++ b/configure
@@ -709,6 +709,7 @@ UUID_EXTRA_OBJS
 with_uuid
 with_systemd
 with_selinux
+with_securetransport
 with_openssl
 krb_srvtab
 with_python
@@ -838,6 +839,7 @@ with_bsd_auth
 with_ldap
 with_bonjour
 with_openssl
+with_securetransport
 with_selinux
 with_systemd
 with_readline
@@ -1534,6 +1536,7 @@ Optional Packages:
   --with-ldap             build with LDAP support
   --with-bonjour          build with Bonjour support
   --with-openssl          build with OpenSSL support
+  --with-securetransport  build with Apple Secure Transport support
   --with-selinux          build with SELinux support
   --with-systemd          build with systemd support
   --without-readline      do not use GNU Readline nor BSD Libedit for editing
@@ -6052,6 +6055,41 @@ $as_echo "$with_openssl" >&6; }
 
 
 #
+# Apple Secure Transport
+#
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build with Apple Secure Transport support" >&5
+$as_echo_n "checking whether to build with Apple Secure Transport support... " >&6; }
+
+
+
+# Check whether --with-securetransport was given.
+if test "${with_securetransport+set}" = set; then :
+  withval=$with_securetransport;
+  case $withval in
+    yes)
+
+$as_echo "#define USE_SECURETRANSPORT 1" >>confdefs.h
+
+      ;;
+    no)
+      :
+      ;;
+    *)
+      as_fn_error $? "no argument expected for --with-securetransport option" "$LINENO" 5
+      ;;
+  esac
+
+else
+  with_securetransport=no
+
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_securetransport" >&5
+$as_echo "$with_securetransport" >&6; }
+
+
+#
 # SELinux
 #
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build with SELinux support" >&5
@@ -11017,6 +11055,25 @@ fi
 
 fi
 
+if test "$with_securetransport" = yes ; then
+  ac_fn_c_check_header_mongrel "$LINENO" "Security/Security.h" "ac_cv_header_Security_Security_h" "$ac_includes_default"
+if test "x$ac_cv_header_Security_Security_h" = xyes; then :
+
+else
+  as_fn_error $? "header file <Security/Security.h> is required for Apple Secure Transport" "$LINENO" 5
+fi
+
+
+  ac_fn_c_check_header_mongrel "$LINENO" "CoreFoundation/CoreFoundation.h" "ac_cv_header_CoreFoundation_CoreFoundation_h" "$ac_includes_default"
+if test "x$ac_cv_header_CoreFoundation_CoreFoundation_h" = xyes; then :
+
+else
+  as_fn_error $? "header file <CoreFoundation/CoreFoundation.h> is required for Apple Secure Transport" "$LINENO" 5
+fi
+
+
+fi
+
 if test "$with_pam" = yes ; then
   for ac_header in security/pam_appl.h
 do :
diff --git a/configure.in b/configure.in
index a0f0f85add..ed4ac073aa 100644
--- a/configure.in
+++ b/configure.in
@@ -735,6 +735,15 @@ AC_MSG_RESULT([$with_openssl])
 AC_SUBST(with_openssl)
 
 #
+# Apple Secure Transport
+#
+AC_MSG_CHECKING([whether to build with Apple Secure Transport support])
+PGAC_ARG_BOOL(with, securetransport, no, [build with Apple Secure Transport support],
+			  [AC_DEFINE([USE_SECURETRANSPORT], 1, [Define to build with Apple Secure Transport support.  (--with-securetransport)])])
+AC_MSG_RESULT([$with_securetransport])
+AC_SUBST(with_securetransport)
+
+#
 # SELinux
 #
 AC_MSG_CHECKING([whether to build with SELinux support])
@@ -1255,6 +1264,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 Apple Secure Transport])])
+  AC_CHECK_HEADER(CoreFoundation/CoreFoundation.h, [], [AC_MSG_ERROR([header file <CoreFoundation/CoreFoundation.h> is required for Apple 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 0d3f8ca950..76a4af7116 100644
--- a/src/Makefile.global.in
+++ b/src/Makefile.global.in
@@ -184,6 +184,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/backend/Makefile b/src/backend/Makefile
index bce9d2c3eb..9c4b248185 100644
--- a/src/backend/Makefile
+++ b/src/backend/Makefile
@@ -49,6 +49,10 @@ ifeq ($(with_systemd),yes)
 LIBS += -lsystemd
 endif
 
+ifeq ($(with_securetransport),yes)
+LDFLAGS += -framework CoreFoundation -framework Security
+endif
+
 ##########################################################################
 
 all: submake-libpgport submake-schemapg postgres $(POSTGRES_IMP)
diff --git a/src/backend/libpq/Makefile b/src/backend/libpq/Makefile
index 7fa2b02743..3a835235b9 100644
--- a/src/backend/libpq/Makefile
+++ b/src/backend/libpq/Makefile
@@ -21,4 +21,9 @@ ifeq ($(with_openssl),yes)
 OBJS += be-secure-openssl.o
 endif
 
+ifeq ($(with_securetransport),yes)
+OBJS += be-secure-securetransport.o
+override CFLAGS += -framework Security -framework CoreFoundation
+endif
+
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/libpq/be-secure-securetransport.c b/src/backend/libpq/be-secure-securetransport.c
new file mode 100644
index 0000000000..88368aae98
--- /dev/null
+++ b/src/backend/libpq/be-secure-securetransport.c
@@ -0,0 +1,1028 @@
+/*-------------------------------------------------------------------------
+ *
+ * be-secure-securetransport.c
+ *	  Apple Secure Transport support
+ *
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * TODO:
+ *		- Load DH keys from file
+ *		- It would be good to be able to set "not applicable" on some options
+ *		  like compression which isn't supported in Secure Transport (and most
+ *		  likely any other SSL libraries supported in the future).
+ *		- Support memory allocation in Secure Transport via a custom Core
+ *		  Foundation allocator which is backed by a MemoryContext? Not sure it
+ *		  would be possible but would be interested to investigate.
+ *
+ * IDENTIFICATION
+ *	  src/backend/libpq/be-secure-securetransport.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include <sys/stat.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#endif
+
+#include "libpq/libpq.h"
+#include "miscadmin.h"
+#include "storage/fd.h"
+#include "storage/latch.h"
+#include "tcop/tcopprot.h"
+#include "utils/backend_random.h"
+#include "utils/memutils.h"
+
+/*
+ * TODO: This dance is required due to collisions in the CoreFoundation
+ * headers. How to handle it properly?
+ */
+#define pg_ACL_DELETE ACL_DELETE
+#define pg_ACL_EXECUTE ACL_EXECUTE
+#undef ACL_EXECUTE
+#undef ACL_DELETE
+#define Size pg_Size
+#define uint64 pg_uint64
+#define bool pg_bool
+#include <Security/cssmerr.h>
+#include <Security/Security.h>
+#include <Security/SecureTransport.h>
+#include "common/securetransport.h"
+#undef uint64
+#undef bool
+#undef Size
+#undef ACL_DELETE
+#undef ACL_EXECUTE
+#define pg_uint64 uint64
+#define pg_bool bool
+#define pg_Size Size
+#define ACL_DELETE pg_ACL_DELETE
+#define ACL_EXECUTE pg_ACL_EXECUTE
+
+#ifndef errSecUnknownFormat
+#define errSecUnknownFormat -25257
+#endif
+
+/* ------------------------------------------------------------ */
+/*				Struct definitions and Static variables			*/
+/* ------------------------------------------------------------ */
+
+/*
+ * For Secure Transport API functions we rely on SecCopyErrorMessageString()
+ * which will provide a human readable error message for the individual error
+ * statuses. For our static functions, we mimic the behaviour by passing
+ * errSecInternalError and setting the error message in internal_err.
+ */
+#define ERR_MSG_LEN 128
+static char internal_err[ERR_MSG_LEN];
+
+/* ------------------------------------------------------------ */
+/*							Prototypes							*/
+/* ------------------------------------------------------------ */
+
+extern SecIdentityRef SecIdentityCreate(CFAllocatorRef allocator,
+										SecCertificateRef certificate,
+										SecKeyRef privateKey);
+
+static OSStatus load_certificate(char *name, CFArrayRef *cert_array);
+static void load_key(char *name, CFArrayRef *out);
+
+static char * pg_SSLerrmessage(OSStatus status);
+static OSStatus pg_SSLSocketWrite(SSLConnectionRef conn, const void *data, size_t *len);
+static OSStatus pg_SSLSocketRead(SSLConnectionRef conn, void *data, size_t *len);
+
+/* ------------------------------------------------------------ */
+/*					Hardcoded DH parameters						*/
+/* ------------------------------------------------------------ */
+
+/*
+ * Secure Transport doesn't support setting different keysizes for DH like
+ * the OpenSSL callback, instead a single keysize is set and there doesn't
+ * seem to be any negotiation from that?
+ */
+static const uint8_t file_dh2048[] =
+	"\x30\x82\x01\x08\x02\x82\x01\x01\x00\xa3\x09\x9b\x20\x73\xdd\x59"
+	"\x1b\x91\x05\x5b\x6c\x5f\xe7\xd7\xea\xaa\x01\x73\xe3\xf5\x89\xbe"
+	"\xc7\xb5\x38\x12\xf5\x28\x40\x03\x32\x89\x48\x39\x77\xb7\xa3\xa0"
+	"\x83\x5f\xff\xbe\x03\xe2\xa5\xf6\x64\xb7\xba\xbd\xb7\xeb\x57\x42"
+	"\xe3\x16\x12\xc3\x9d\xba\x06\xf0\xbb\xab\x13\x98\x0a\xcc\x6c\x63"
+	"\xb9\xca\xb0\x39\x86\x9e\xb7\xa0\x23\x96\xd5\xce\x24\x44\x2a\x05"
+	"\x1c\xe5\x69\x5d\xd1\x83\x8c\xc1\x92\xb1\xd6\x18\xe5\x4e\xc8\xeb"
+	"\x21\xe8\x16\x87\x69\x4f\x86\x95\x25\x10\xdb\x1e\x49\x1c\x80\x3e"
+	"\x9a\x3d\x43\x66\xf7\x45\x0d\x37\x61\x37\xad\xce\x31\xec\x3b\xbd"
+	"\x55\x08\xe9\xb2\x97\xf0\xfc\x59\x8e\xd3\x73\xe2\x4a\x9b\x58\xbb"
+	"\x0a\x34\xb7\xea\x42\x94\xf9\xf5\xba\xf2\x06\xd9\xe6\xf1\xa7\x4a"
+	"\x6f\xd4\x72\x1f\x8d\x20\x63\x85\x29\xe5\x90\x59\xc0\x36\x3e\x16"
+	"\x5c\xa4\x46\xac\x44\x8c\x89\x00\x5d\xa2\xe9\x5b\xf0\xe4\x8c\xea"
+	"\xa6\x37\xac\xf8\x2a\x33\xbb\x39\xc6\xdf\x14\x96\x64\x13\x9e\x99"
+	"\xd8\xdd\x94\x61\xfe\xa1\x87\x48\xb6\x65\x69\xc7\xe7\x49\x53\xcf"
+	"\xa8\xa1\xc0\x9d\xf6\x7c\x29\xfc\xb2\x74\xb1\x2d\xe5\x0c\xd0\x32"
+	"\xc4\x35\x85\x82\x30\x07\x60\x01\x93\x02\x01\x02";
+
+
+/* ------------------------------------------------------------ */
+/*							Backend API							*/
+/* ------------------------------------------------------------ */
+
+int
+be_tls_init(bool isServerStart)
+{
+	memset(internal_err, '\0', sizeof(internal_err));
+
+	/*
+	 * This is where we'd like to load and parse certificates and private keys
+	 * for the connection, but since Secure Transport will spawn threads deep
+	 * inside the API we must postpone this until inside a backend. This means
+	 * that we won't fail on an incorrect certificate chain until a connection
+	 * is attempted, unlike with OpenSSL where we fail immediately on server
+	 * startup.
+	 */
+
+	return 0;
+}
+
+/*
+ * be_tls_destroy
+ *		Tear down global Secure Transport structures and return resources.
+ */
+void
+be_tls_destroy(void)
+{
+	ssl_loaded_verify_locations = false;
+}
+
+/*
+ * bt_tls_open_server
+ *		Attempt to negotiate a secure connection
+ */
+int
+be_tls_open_server(Port *port)
+{
+	OSStatus			status;
+	SecTrustRef			trust;
+	SecTrustResultType	trust_eval;
+	SecIdentityRef		identity;
+	CFArrayRef			root_certificates;
+	CFArrayRef			certificates;
+	CFArrayRef			keys;
+	CFMutableArrayRef	chain;
+
+	Assert(!port->ssl);
+
+	status = load_certificate(ssl_cert_file, &certificates);
+	if (status != noErr)
+		ereport(COMMERROR,
+				(errmsg("could not load server certificate \"%s\": \"%s\"",
+						ssl_cert_file, pg_SSLerrmessage(status))));
+
+	load_key(ssl_key_file, &keys);
+
+	/*
+	 * We now have a certificate and either a private key, or a search path
+	 * which should contain it.
+	 */
+	identity = SecIdentityCreate(NULL,
+								 (SecCertificateRef) CFArrayGetValueAtIndex(certificates, 0),
+								 (SecKeyRef) CFArrayGetValueAtIndex(keys, 0));
+	if (identity == NULL)
+		ereport(COMMERROR,
+				(errmsg("could not create identity: \"%s\"",
+				 pg_SSLerrmessage(status))));
+
+	/*
+	 * SSLSetCertificate() sets the certificate(s) to use for the connection.
+	 * The first element in the passed array is required to be the identity
+	 * with elements 1..n being certificates.
+	 */
+	chain = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+	CFRetain(identity);
+	CFArrayInsertValueAtIndex(chain, 0, identity);
+	CFArrayAppendArray(chain, certificates,
+					   CFRangeMake(0, CFArrayGetCount(certificates)));
+
+	/*
+	 * Load the Certificate Authority if configured
+	 */
+	if (ssl_ca_file[0])
+	{
+		status = load_certificate(ssl_ca_file, &root_certificates);
+		if (status == noErr)
+		{
+			CFArrayAppendArray(chain, root_certificates,
+							   CFRangeMake(0, CFArrayGetCount(root_certificates)));
+
+			ssl_loaded_verify_locations = true;
+		}
+		else
+		{
+			ereport(LOG,
+					(errmsg("could not load root certificate \"%s\": \"%s\"",
+					 ssl_ca_file, pg_SSLerrmessage(status))));
+
+			ssl_loaded_verify_locations = false;
+		}
+	}
+	else
+		ssl_loaded_verify_locations = false;
+
+	/*
+	 * Certificate Revocation List are not supported in the Secure Transport
+	 * API
+	 */
+	if (ssl_crl_file[0])
+		ereport(FATAL,
+				(errmsg("CRL files not supported with Secure Transport")));
+
+	port->ssl = (void *) SSLCreateContext(NULL, kSSLServerSide, kSSLStreamType);
+	if (!port->ssl)
+		ereport(COMMERROR,
+				(errmsg("could not create SSL context")));
+
+	port->ssl_in_use = true;
+	port->ssl_buffered = 0;
+
+	/*
+	 * We use kTryAuthenticate here since we don't know which sslmode the
+	 * client is using. If we were to use kAlwaysAuthenticate then sslmode
+	 * require won't work as intended.
+	 */
+	if (ssl_loaded_verify_locations)
+		SSLSetClientSideAuthenticate((SSLContextRef) port->ssl, kTryAuthenticate);
+
+	/*
+	 * TODO: This loads a pregenerated DH parameter, code for loading DH from a
+	 * specified file needs to be added. In Secure Transport, DH is always on
+	 * an in case no parameter has been loaded one with be precomputed
+	 * automatically, so that step is not required to be added. This is of
+	 * course the one step we want to avoid since it consumes a lot of time.
+	 */
+	SSLSetDiffieHellmanParams((SSLContextRef) port->ssl,
+							  file_dh2048, sizeof(file_dh2048));
+
+	status = SSLSetProtocolVersionMin((SSLContextRef) port->ssl,
+									  kTLSProtocol12);
+	if (status != noErr)
+		ereport(COMMERROR,
+				(errmsg("could not set protocol for connection: \"%s\"",
+				 pg_SSLerrmessage(status))));
+
+	status = SSLSetCertificate((SSLContextRef) port->ssl,
+							   (CFArrayRef) chain);
+	if (status != noErr)
+		ereport(COMMERROR,
+				(errmsg("could not set certificate for connection: \"%s\"",
+				 pg_SSLerrmessage(status))));
+
+	status = SSLSetIOFuncs((SSLContextRef) port->ssl,
+						   pg_SSLSocketRead,
+						   pg_SSLSocketWrite);
+	if (status != noErr)
+		ereport(COMMERROR,
+				(errmsg("could not set SSL IO functions: \"%s\"",
+				 pg_SSLerrmessage(status))));
+
+	status = SSLSetSessionOption((SSLContextRef) port->ssl,
+								 kSSLSessionOptionBreakOnClientAuth, true);
+	if (status != noErr)
+		ereport(COMMERROR,
+				(errmsg("could not set SSL certificate validation: \"%s\"",
+				 pg_SSLerrmessage(status))));
+
+	status = SSLSetConnection((SSLContextRef) port->ssl, port);
+	if (status != noErr)
+		ereport(COMMERROR,
+				(errmsg("could not establish SSL connection: \"%s\"",
+				 pg_SSLerrmessage(status))));
+
+	/*
+	 * Perform handshake
+	 */
+	for (;;)
+	{
+		status = SSLHandshake((SSLContextRef) port->ssl);
+		if (status == noErr)
+			break;
+
+		if (status == errSSLWouldBlock)
+			continue;
+
+		if (status == errSSLClosedAbort || status == errSSLClosedGraceful)
+			return -1;
+
+		if (status == errSSLPeerAuthCompleted)
+		{
+			status = SSLCopyPeerTrust((SSLContextRef) port->ssl, &trust);
+			if (status != noErr || trust == NULL)
+			{
+				ereport(WARNING,
+					(errmsg("SSLCopyPeerTrust returned: \"%s\"",
+					 pg_SSLerrmessage(status))));
+				port->peer_cert_valid = false;
+				return 0;
+			}
+
+			if (ssl_loaded_verify_locations)
+			{
+				status = SecTrustSetAnchorCertificates(trust, root_certificates);
+				if (status != noErr)
+				{
+					ereport(WARNING,
+						(errmsg("SecTrustSetAnchorCertificates returned: \"%s\"",
+						 pg_SSLerrmessage(status))));
+					return -1;
+				}
+
+				status = SecTrustSetAnchorCertificatesOnly(trust, false);
+				if (status != noErr)
+				{
+					ereport(WARNING,
+						(errmsg("SecTrustSetAnchorCertificatesOnly returned: \"%s\"",
+						 pg_SSLerrmessage(status))));
+					return -1;
+				}
+			}
+
+			trust_eval = 0;
+			status = SecTrustEvaluate(trust, &trust_eval);
+			if (status != noErr)
+			{
+				ereport(WARNING,
+					(errmsg("SecTrustEvaluate failed, returned: \"%s\"",
+					 pg_SSLerrmessage(status))));
+				return -1;
+			}
+
+			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:
+					port->peer_cert_valid = true;
+					break;
+
+				/*
+				 * 'RecoverableTrustFailure' indicates that the certificate was
+				 * rejected but might be trusted with minor changes to the eval
+				 * context (ignoring expired certificate etc). In the frontend
+				 * we can in some circumstances allow this, but in the backend
+				 * this always means that the client certificate is considered
+				 * untrusted.
+				 */
+				case kSecTrustResultRecoverableTrustFailure:
+					port->peer_cert_valid = false;
+					break;
+
+				/*
+				 * Treat all other cases as rejection without further
+				 * questioning.
+				 */
+				default:
+					port->peer_cert_valid = false;
+					break;
+			}
+
+			if (port->peer_cert_valid)
+			{
+				SecCertificateRef usercert = SecTrustGetCertificateAtIndex(trust, 0L);
+
+				CFStringRef usercert_cn;
+				SecCertificateCopyCommonName(usercert, &usercert_cn);
+				port->peer_cn = pstrdup(CFStringGetCStringPtr(usercert_cn, kCFStringEncodingUTF8));
+
+				CFRelease(usercert_cn);
+			}
+
+			CFRelease(trust);
+		}
+	}
+
+	if (status != noErr)
+		return -1;
+
+	return 0;
+}
+
+/*
+ *	load_key
+ *		Extracts a key from a PEM file on the filesystem
+ *
+ * Loads a private key from the specified filename. Unless the key loads this
+ * will not return but will error out.
+ */
+static void
+load_key(char *name, CFArrayRef *out)
+{
+	OSStatus			status;
+	struct stat			stat_buf;
+	int					ret;
+	UInt8			   *buf;
+	FILE			   *fd;
+	CFDataRef			data;
+	SecExternalFormat	format;
+	SecExternalItemType	type;
+	CFStringRef			path;
+
+	ret = stat(name, &stat_buf);
+	if (ret != 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("could not load private key \"%s\": unable to stat",
+						name)));
+
+	if (!S_ISREG(stat_buf.st_mode))
+		ereport(ERROR,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("could not load private key \"%s\": not a regular file",
+						name)));
+
+	/*
+	 * Require no public access to the key file. If the file is owned by us,
+	 * require mode 0600 or less. If owned by root, require 0640 or less to
+	 * allow read access through our gid, or a supplementary gid that allows to
+	 * read system-wide certificates.
+	 */
+	if ((stat_buf.st_uid == geteuid() && stat_buf.st_mode & (S_IRWXG | S_IRWXO)) ||
+		(stat_buf.st_uid == 0 && stat_buf.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)))
+		ereport(ERROR,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("private key file \"%s\" has group or world access",
+						name),
+				 errdetail("File must have permissions u=rw (0600) or less "
+						   "if owned by the database user, or permissions "
+						   "u=rw,g=r (0640) or less if owned by root.")));
+
+	if ((fd = AllocateFile(name, "r")) == NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("could not load private key \"%s\": unable to open",
+						name)));
+
+	buf = palloc(stat_buf.st_size);
+
+	ret = fread(buf, 1, stat_buf.st_size, fd);
+	FreeFile(fd);
+
+	if (ret != stat_buf.st_size)
+		ereport(ERROR,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("could not load private key \"%s\": unable to read",
+						name)));
+
+	type = kSecItemTypePrivateKey;
+	format = kSecFormatPEMSequence;
+	path = CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8);
+	data = CFDataCreate(NULL, buf, stat_buf.st_size);
+
+	SecItemImportExportKeyParameters params;
+	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;
+
+	status = SecItemImport(data, path, &format, &type, 0, &params, NULL, out);
+
+	CFRelease(path);
+	CFRelease(data);
+
+	if (status != noErr)
+		ereport(ERROR,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("could not load private key \"%s\": \"%s\"",
+						name, pg_SSLerrmessage(status))));
+}
+
+/*
+ *	load_certificate
+ *		Extracts a certificate from a PEM file on the filesystem
+ *
+ * TODO: figure out better returncodes
+ */
+static OSStatus
+load_certificate(char *name, CFArrayRef *cert_array)
+{
+	struct stat			stat_buf;
+	int					ret;
+	UInt8			   *buf;
+	FILE			   *fd;
+	CFDataRef			data;
+	SecExternalFormat	format;
+	SecExternalItemType	type;
+	CFStringRef			path;
+	OSStatus			status;
+
+	/*
+	 * If the configured ssl_cert_file filename is set to a non-existing
+	 * file, assume it's referencing a Keychain label and attempt to load
+	 * the certificate from the Keychain instead.
+	 */
+	ret = stat(name, &stat_buf);
+	if (ret != 0 && errno == ENOENT)
+	{
+		/*
+		 * TODO: Do we want to search keychains for certificates serverside
+		 * like we do clientside, or do we want to stick to a single way to
+		 * configure the server? Since CRL files aren't supported outside
+		 * keychains I guess we need to, but worth a discussion.
+		 */
+		return errSecInternalError;
+	}
+	else if (ret == 0 && S_ISREG(stat_buf.st_mode))
+	{
+		if ((fd = AllocateFile(name, "r")) == NULL)
+			return errSecInternalError;
+
+		buf = palloc(stat_buf.st_size);
+		ret = fread(buf, 1, stat_buf.st_size, fd);
+		FreeFile(fd);
+
+		if (ret != stat_buf.st_size)
+			return errSecInternalError;
+
+		type = kSecItemTypeCertificate;
+		format = kSecFormatPEMSequence;
+		path = CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8);
+		data = CFDataCreate(NULL, buf, stat_buf.st_size);
+
+		status = SecItemImport(data, path, &format, &type, 0, NULL, NULL, cert_array);
+		pfree(buf);
+
+		return status;
+	}
+
+	return errSecInternalError;
+}
+
+
+/*
+ *	Close SSL connection.
+ */
+void
+be_tls_close(Port *port)
+{
+	OSStatus		ssl_status;
+
+	if (!port->ssl)
+		return;
+
+	ssl_status = SSLClose((SSLContextRef) port->ssl);
+	if (ssl_status != noErr)
+		ereport(COMMERROR,
+				(errcode(ERRCODE_PROTOCOL_VIOLATION),
+				 errmsg("error in closing SSL connection: %s",
+						pg_SSLerrmessage(ssl_status))));
+
+	CFRelease((SSLContextRef) port->ssl);
+
+	port->ssl = NULL;
+	port->ssl_in_use = false;
+}
+
+/*
+ * be_tls_get_version
+ *		Retrieve the protocol version of the current connection
+ */
+void
+be_tls_get_version(Port *port, char *ptr, size_t len)
+{
+	OSStatus		status;
+	SSLProtocol		protocol;
+
+	if (ptr == NULL || len == 0)
+		return;
+
+	ptr[0] = '\0';
+
+	if (!(SSLContextRef) port->ssl)
+		return;
+
+	status = SSLGetNegotiatedProtocolVersion((SSLContextRef) port->ssl, &protocol);
+	if (status == noErr)
+	{
+		switch (protocol)
+		{
+			case kTLSProtocol11:
+				strlcpy(ptr, "TLSv1.1", len);
+				break;
+			case kTLSProtocol12:
+				strlcpy(ptr, "TLSv1.2", len);
+				break;
+			default:
+				strlcpy(ptr, "unknown", len);
+				break;
+		}
+	}
+}
+
+/*
+ *	Read data from a secure connection.
+ */
+ssize_t
+be_tls_read(Port *port, void *ptr, size_t len, int *waitfor)
+{
+	size_t			n = 0;
+	ssize_t			ret;
+	OSStatus		read_status;
+	SSLContextRef	ssl = (SSLContextRef) port->ssl;
+
+	errno = 0;
+
+	if (len <= 0)
+		return 0;
+
+	read_status = SSLRead(ssl, ptr, len, &n);
+	switch (read_status)
+	{
+		case noErr:
+			ret = n;
+			break;
+
+		/* Function is blocked, waiting for I/O */
+		case errSSLWouldBlock:
+			if (port->ssl_buffered)
+				*waitfor = WL_SOCKET_WRITEABLE;
+			else
+				*waitfor = WL_SOCKET_READABLE;
+
+			errno = EWOULDBLOCK;
+			if (n == 0)
+				ret = -1;
+			else
+				ret = n;
+
+			break;
+
+		case errSSLClosedGraceful:
+			ret = 0;
+			break;
+
+		/*
+		 * If the connection was closed for an unforeseen reason, return error
+		 * and set errno such that the caller can raise an appropriate ereport
+		 */
+		case errSSLClosedNoNotify:
+		case errSSLClosedAbort:
+			ret = -1;
+			errno = ECONNRESET;
+			break;
+
+		default:
+			ret = -1;
+			ereport(COMMERROR,
+					(errcode(ERRCODE_PROTOCOL_VIOLATION),
+					 errmsg("SSL error: %s",
+							pg_SSLerrmessage(read_status))));
+			break;
+	}
+
+	return ret;
+}
+
+/*
+ *	Write data to a secure connection.
+ */
+ssize_t
+be_tls_write(Port *port, void *ptr, size_t len, int *waitfor)
+{
+	size_t		n = 0;
+	OSStatus	write_status;
+
+	errno = 0;
+
+	if (len == 0)
+		return 0;
+
+	/*
+	 * SSLWrite returns the number of bytes written in the 'n' argument. This
+	 * however can be data either actually written to the socket, or buffered
+	 * in the context. In the latter case SSLWrite will return errSSLWouldBlock
+	 * and we need to call it with no new data (NULL) to drain the buffer on to
+	 * the socket. We track the buffer in ssl_buffered and clear that when all
+	 * data has been drained.
+	 */
+	if (port->ssl_buffered > 0)
+	{
+		write_status = SSLWrite((SSLContextRef) port->ssl, NULL, 0, &n);
+
+		if (write_status == noErr)
+		{
+			n = port->ssl_buffered;
+			port->ssl_buffered = 0;
+		}
+		else if (write_status == errSSLWouldBlock || write_status == -1)
+		{
+			n = -1;
+			errno = EINTR;
+		}
+		else
+		{
+			n = -1;
+			errno = ECONNRESET;
+		}
+	}
+	else
+	{
+		write_status = SSLWrite((SSLContextRef) port->ssl, ptr, len, &n);
+
+		switch (write_status)
+		{
+			case noErr:
+				break;
+
+			/*
+			 * The data was buffered in the context rather than written to the
+			 * socket. Track this and repeatedly call SSLWrite to drain the
+			 * buffer. See comment above.
+			 */
+			case errSSLWouldBlock:
+				port->ssl_buffered = len;
+				n = 0;
+#ifdef EAGAIN
+				errno = EAGAIN;
+#else
+				errno = EINTR;
+#endif
+				break;
+
+			/* Clean disconnections */
+			case errSSLClosedNoNotify:
+				/* fall through */
+			case errSSLClosedGraceful:
+				errno = ECONNRESET;
+				n = -1;
+				break;
+
+			default:
+				errno = ECONNRESET;
+				n = -1;
+				break;
+		}
+	}
+
+	return n;
+}
+
+/*
+ * be_tls_get_cipher_bits
+ *		Returns the number of bits in the encryption for the current cipher
+ *
+ * Note: In case of errors, this returns 0 to match the OpenSSL implementation.
+ * A NULL encryption will however also return 0 making it complicated to
+ * differentiate between the two.
+ */
+int
+be_tls_get_cipher_bits(Port *port)
+{
+	OSStatus		status;
+	SSLCipherSuite	cipher;
+
+	if (!(SSLContextRef) port->ssl)
+		return 0;
+
+	status = SSLGetNegotiatedCipher((SSLContextRef) port->ssl, &cipher);
+	if (status != noErr)
+		return 0;
+
+	return pg_SSLcipherbits(cipher);
+}
+
+void
+be_tls_get_peerdn_name(Port *port, char *ptr, size_t len)
+{
+	OSStatus			status;
+	SecTrustRef			trust;
+	SecCertificateRef	cert;
+	CFStringRef			dn_str;
+
+	if (!ptr || len == 0)
+		return;
+
+	ptr[0] = '\0';
+
+	if (!(SSLContextRef) port->ssl)
+		return;
+
+	status = SSLCopyPeerTrust((SSLContextRef) port->ssl, &trust);
+	if (status == noErr)
+	{
+		/*
+		 * TODO: copy the certificate parts with SecCertificateCopyValues and
+		 * parse the OIDs to build up the DN
+		 */
+		cert = SecTrustGetCertificateAtIndex(trust, 0);
+		dn_str = SecCertificateCopyLongDescription(NULL, cert, NULL);
+		if (dn_str)
+		{
+			strlcpy(ptr, CFStringGetCStringPtr(dn_str, kCFStringEncodingASCII), len);
+			CFRelease(dn_str);
+		}
+
+		CFRelease(trust);
+	}
+}
+
+/*
+ * be_tls_get_cipher
+ *		Return the negotiated ciphersuite for the current connection.
+ *
+ * Returns NULL in case we weren't able to either get the negotiated cipher, or
+ * translate it into a human readable string.
+ */
+void
+be_tls_get_cipher(Port *port, char *ptr, size_t len)
+{
+	OSStatus		status;
+	SSLCipherSuite	cipher;
+	const char	   *cipher_name;
+
+	if (!ptr || len == 0)
+		return;
+
+	ptr[0] = '\0';
+
+	if (!(SSLContextRef) port->ssl)
+		return;
+
+	status = SSLGetNegotiatedCipher((SSLContextRef) port->ssl, &cipher);
+	if (status != noErr)
+		return;
+
+	cipher_name = pg_SSLciphername(cipher);
+	if (cipher_name != NULL)
+		strlcpy(ptr, cipher_name, len);
+}
+
+/*
+ * be_tls_get_compression
+ *		Retrieve and return whether compression is used for the	current
+ *		connection.
+ *
+ * Since Secure Transport doesn't support compression at all, always return
+ * false here. Ideally we should be able to tell the caller that the option
+ * isn't applicable rather than return false, but the current SSL support
+ * doesn't allow for that.
+ */
+bool
+be_tls_get_compression(Port *port)
+{
+	return false;
+}
+
+/* ------------------------------------------------------------ */
+/*				Internal functions - Translation				*/
+/* ------------------------------------------------------------ */
+
+/*
+ * pg_SSLerrmessage
+ *		Create and return a human readable error message given
+ *		the specified status code
+ *
+ * While only interesting to use for error cases, the function will return a
+ * translation for non-error statuses as well like noErr and errSecSuccess.
+ */
+static char *
+pg_SSLerrmessage(OSStatus status)
+{
+	CFStringRef		err_msg;
+	char		   *err_buf;
+
+	/*
+	 * While errSecUnknownFormat has been defined as -25257 at least since 10.8
+	 * Lion, there still is no translation for it in 10.11 El Capitan, so we
+	 * maintain our own
+	 */
+	if (status == errSecUnknownFormat)
+		return pstrdup(_("The item you are trying to import has an unknown format."));
+
+	/*
+	 * If the error is internal, and we have an error message in the internal
+	 * buffer, then return that error and clear the internal buffer.
+	 */
+	if (status == errSecInternalError && internal_err[0])
+	{
+		err_buf = pstrdup(internal_err);
+		memset(internal_err, '\0', ERR_MSG_LEN);
+	}
+	else
+	{
+		err_msg = SecCopyErrorMessageString(status, NULL);
+
+		if (err_msg)
+		{
+			err_buf = pstrdup(CFStringGetCStringPtr(err_msg,
+													kCFStringEncodingUTF8));
+			CFRelease(err_msg);
+		}
+		else
+			err_buf = pstrdup(_("unknown SSL error"));
+	}
+
+	return err_buf;
+}
+
+/* ------------------------------------------------------------ */
+/*				Internal functions - Socket IO					*/
+/* ------------------------------------------------------------ */
+
+/*
+ *	pg_SSLSocketRead
+ *
+ * Callback for reading data from the connection. When entering the function,
+ * len is set to the number of bytes requested. Upon leaving, len should be
+ * overwritten with the actual number of bytes read.
+ */
+static OSStatus
+pg_SSLSocketRead(SSLConnectionRef conn, void *data, size_t *len)
+{
+	OSStatus	status;
+	int			res;
+
+	res = secure_raw_read((Port *) conn, data, *len);
+
+	if (res < 0)
+	{
+		switch (errno)
+		{
+#ifdef EAGAIN
+			case EAGAIN:
+#endif
+#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
+			case EWOULDBLOCK:
+#endif
+			case EINTR:
+				status = errSSLWouldBlock;
+				break;
+			case ENOENT:
+				status =  errSSLClosedGraceful;
+				break;
+
+			default:
+				status = errSSLClosedAbort;
+				break;
+		}
+
+		*len = 0;
+	}
+	else
+	{
+		status = noErr;
+		*len = res;
+	}
+
+	return status;
+}
+
+static OSStatus
+pg_SSLSocketWrite(SSLConnectionRef conn, const void *data, size_t *len)
+{
+	OSStatus	status;
+	int			res;
+	Port	   *port = (Port *) conn;
+
+	res = secure_raw_write(port, data, *len);
+
+	if (res < 0)
+	{
+		switch (errno)
+		{
+#ifdef EAGAIN
+			case EAGAIN:
+#endif
+#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
+			case EWOULDBLOCK:
+#endif
+			case EINTR:
+				status = errSSLWouldBlock;
+				break;
+
+			default:
+				status = errSSLClosedAbort;
+				break;
+		}
+
+		*len = res;
+	}
+	else
+	{
+		status = noErr;
+		*len = res;
+	}
+
+	return status;
+}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 246fea8693..c026dbad12 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -501,6 +501,7 @@ static char *locale_ctype;
 static char *server_encoding_string;
 static char *server_version_string;
 static int	server_version_num;
+static char *ssl_library_string;
 static char *timezone_string;
 static char *log_timezone_string;
 static char *timezone_abbreviations_string;
@@ -3298,6 +3299,18 @@ static struct config_string ConfigureNamesString[] =
 	},
 
 	{
+		/* Can't be set in postgresql.conf */
+		{"ssl_library", PGC_INTERNAL, PRESET_OPTIONS,
+			gettext_noop("Shows the SSL library used."),
+			NULL,
+			GUC_REPORT | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
+		},
+		&ssl_library_string,
+		SSL_LIBRARY,
+		NULL, NULL, NULL
+	},
+
+	{
 		/* Not for general use --- used by SET ROLE */
 		{"role", PGC_USERSET, UNGROUPED,
 			gettext_noop("Sets the current role."),
diff --git a/src/include/common/securetransport.h b/src/include/common/securetransport.h
new file mode 100644
index 0000000000..997c504e41
--- /dev/null
+++ b/src/include/common/securetransport.h
@@ -0,0 +1,510 @@
+/*-------------------------------------------------------------------------
+ *
+ * securetransport_common.h
+ *	  Apple Secure Transport support
+ *
+ * These definitions are used by both frontend and backend code.
+ *
+ * Copyright (c) 2017, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *        src/include/common/securetransport.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SECURETRANSPORT_H
+#define SECURETRANSPORT_H
+
+#include <Security/SecureTransport.h>
+
+/*
+ * pg_SSLciphername
+ *
+ * Translate a 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.
+ *
+ * The SSLCipherSuite enum is defined in Security/CipherSuite.h
+ *
+ * This only removes the TLS_ portion of the SSLCipherSuite enum label for the
+ * ciphers to match what most Secure Transport implementations seem to be doing
+ */
+static const char *
+pg_SSLciphername(SSLCipherSuite cipher)
+{
+	switch (cipher)
+	{
+		case TLS_NULL_WITH_NULL_NULL:
+			return "NULL";
+
+		/* TLS addenda using AES, per RFC 3268 */
+		case TLS_RSA_WITH_AES_128_CBC_SHA:
+			return "RSA_WITH_AES_128_CBC_SHA";
+		case TLS_DH_DSS_WITH_AES_128_CBC_SHA:
+			return "DH_DSS_WITH_AES_128_CBC_SHA";
+		case TLS_DH_RSA_WITH_AES_128_CBC_SHA:
+			return "DH_RSA_WITH_AES_128_CBC_SHA";
+		case TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
+			return "DHE_DSS_WITH_AES_128_CBC_SHA";
+		case TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+			return "DHE_RSA_WITH_AES_128_CBC_SHA";
+		case TLS_DH_anon_WITH_AES_128_CBC_SHA:
+			return "DH_anon_WITH_AES_128_CBC_SHA";
+		case TLS_RSA_WITH_AES_256_CBC_SHA:
+			return "RSA_WITH_AES_256_CBC_SHA";
+		case TLS_DH_DSS_WITH_AES_256_CBC_SHA:
+			return "DH_DSS_WITH_AES_256_CBC_SHA";
+		case TLS_DH_RSA_WITH_AES_256_CBC_SHA:
+			return "DH_RSA_WITH_AES_256_CBC_SHA";
+		case TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
+			return "DHE_DSS_WITH_AES_256_CBC_SHA";
+		case TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+			return "DHE_RSA_WITH_AES_256_CBC_SHA";
+		case TLS_DH_anon_WITH_AES_256_CBC_SHA:
+			return "DH_anon_WITH_AES_256_CBC_SHA";
+
+		/* ECDSA addenda, RFC 4492 */
+		case TLS_ECDH_ECDSA_WITH_NULL_SHA:
+			return "ECDH_ECDSA_WITH_NULL_SHA";
+		case TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
+			return "ECDH_ECDSA_WITH_RC4_128_SHA";
+		case TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
+			return "ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA";
+		case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
+			return "ECDH_ECDSA_WITH_AES_128_CBC_SHA";
+		case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
+			return "ECDH_ECDSA_WITH_AES_256_CBC_SHA";
+		case TLS_ECDHE_ECDSA_WITH_NULL_SHA:
+			return "ECDHE_ECDSA_WITH_NULL_SHA";
+		case TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
+			return "ECDHE_ECDSA_WITH_RC4_128_SHA";
+		case TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
+			return "ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA";
+		case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
+			return "ECDHE_ECDSA_WITH_AES_128_CBC_SHA";
+		case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+			return "ECDHE_ECDSA_WITH_AES_256_CBC_SHA";
+		case TLS_ECDH_RSA_WITH_NULL_SHA:
+			return "ECDH_RSA_WITH_NULL_SHA";
+		case TLS_ECDH_RSA_WITH_RC4_128_SHA:
+			return "ECDH_RSA_WITH_RC4_128_SHA";
+		case TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
+			return "ECDH_RSA_WITH_3DES_EDE_CBC_SHA";
+		case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
+			return "ECDH_RSA_WITH_AES_128_CBC_SHA";
+		case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
+			return "ECDH_RSA_WITH_AES_256_CBC_SHA";
+		case TLS_ECDHE_RSA_WITH_NULL_SHA:
+			return "ECDHE_RSA_WITH_NULL_SHA";
+		case TLS_ECDHE_RSA_WITH_RC4_128_SHA:
+			return "ECDHE_RSA_WITH_RC4_128_SHA";
+		case TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
+			return "ECDHE_RSA_WITH_3DES_EDE_CBC_SHA";
+		case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+			return "ECDHE_RSA_WITH_AES_128_CBC_SHA";
+		case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+			return "ECDHE_RSA_WITH_AES_256_CBC_SHA";
+		case TLS_ECDH_anon_WITH_NULL_SHA:
+			return "ECDH_anon_WITH_NULL_SHA";
+		case TLS_ECDH_anon_WITH_RC4_128_SHA:
+			return "ECDH_anon_WITH_RC4_128_SHA";
+		case TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA:
+			return "ECDH_anon_WITH_3DES_EDE_CBC_SHA";
+		case TLS_ECDH_anon_WITH_AES_128_CBC_SHA:
+			return "ECDH_anon_WITH_AES_128_CBC_SHA";
+		case TLS_ECDH_anon_WITH_AES_256_CBC_SHA:
+			return "ECDH_anon_WITH_AES_256_CBC_SHA";
+
+		/* Server provided RSA certificate for key exchange. */
+		case TLS_RSA_WITH_NULL_MD5:
+			return "RSA_WITH_NULL_MD5";
+		case TLS_RSA_WITH_NULL_SHA:
+			return "RSA_WITH_NULL_SHA";
+		case TLS_RSA_WITH_RC4_128_MD5:
+			return "RSA_WITH_RC4_128_MD5";
+		case TLS_RSA_WITH_RC4_128_SHA:
+			return "RSA_WITH_RC4_128_SHA";
+		case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+			return "RSA_WITH_3DES_EDE_CBC_SHA";
+		case TLS_RSA_WITH_NULL_SHA256:
+			return "RSA_WITH_NULL_SHA256";
+		case TLS_RSA_WITH_AES_128_CBC_SHA256:
+			return "RSA_WITH_AES_128_CBC_SHA256";
+		case TLS_RSA_WITH_AES_256_CBC_SHA256:
+			return "RSA_WITH_AES_256_CBC_SHA256";
+
+		/*
+		 * 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";
+		case TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA:
+			return "DH_RSA_WITH_3DES_EDE_CBC_SHA";
+		case TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
+			return "DHE_DSS_WITH_3DES_EDE_CBC_SHA";
+		case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+			return "DHE_RSA_WITH_3DES_EDE_CBC_SHA";
+		case TLS_DH_DSS_WITH_AES_128_CBC_SHA256:
+			return "DH_DSS_WITH_AES_128_CBC_SHA256";
+		case TLS_DH_RSA_WITH_AES_128_CBC_SHA256:
+			return "DH_RSA_WITH_AES_128_CBC_SHA256";
+		case TLS_DHE_DSS_WITH_AES_128_CBC_SHA256:
+			return "DHE_DSS_WITH_AES_128_CBC_SHA256";
+		case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+			return "DHE_RSA_WITH_AES_128_CBC_SHA256";
+		case TLS_DH_DSS_WITH_AES_256_CBC_SHA256:
+			return "DH_DSS_WITH_AES_256_CBC_SHA256";
+		case TLS_DH_RSA_WITH_AES_256_CBC_SHA256:
+			return "DH_RSA_WITH_AES_256_CBC_SHA256";
+		case TLS_DHE_DSS_WITH_AES_256_CBC_SHA256:
+			return "DHE_DSS_WITH_AES_256_CBC_SHA256";
+		case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+			return "DHE_RSA_WITH_AES_256_CBC_SHA256";
+
+		/* Completely anonymous Diffie-Hellman */
+		case TLS_DH_anon_WITH_RC4_128_MD5:
+			return "DH_anon_WITH_RC4_128_MD5";
+		case TLS_DH_anon_WITH_3DES_EDE_CBC_SHA:
+			return "DH_anon_WITH_3DES_EDE_CBC_SHA";
+		case TLS_DH_anon_WITH_AES_128_CBC_SHA256:
+			return "DH_anon_WITH_AES_128_CBC_SHA256";
+		case TLS_DH_anon_WITH_AES_256_CBC_SHA256:
+			return "DH_anon_WITH_AES_256_CBC_SHA256";
+
+		/* Addendum from RFC 4279, TLS PSK */
+		case TLS_PSK_WITH_RC4_128_SHA:
+			return "PSK_WITH_RC4_128_SHA";
+		case TLS_PSK_WITH_3DES_EDE_CBC_SHA:
+			return "PSK_WITH_3DES_EDE_CBC_SHA";
+		case TLS_PSK_WITH_AES_128_CBC_SHA:
+			return "PSK_WITH_AES_128_CBC_SHA";
+		case TLS_PSK_WITH_AES_256_CBC_SHA:
+			return "PSK_WITH_AES_256_CBC_SHA";
+		case TLS_DHE_PSK_WITH_RC4_128_SHA:
+			return "DHE_PSK_WITH_RC4_128_SHA";
+		case TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
+			return "DHE_PSK_WITH_3DES_EDE_CBC_SHA";
+		case TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
+			return "DHE_PSK_WITH_AES_128_CBC_SHA";
+		case TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
+			return "DHE_PSK_WITH_AES_256_CBC_SHA";
+		case TLS_RSA_PSK_WITH_RC4_128_SHA:
+			return "RSA_PSK_WITH_RC4_128_SHA";
+		case TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA:
+			return "RSA_PSK_WITH_3DES_EDE_CBC_SHA";
+		case TLS_RSA_PSK_WITH_AES_128_CBC_SHA:
+			return "RSA_PSK_WITH_AES_128_CBC_SHA";
+		case TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
+			return "RSA_PSK_WITH_AES_256_CBC_SHA";
+
+		/* RFC 4785, Pre-Shared Key (PSK) Ciphersuites with NULL Encryption */
+		case TLS_PSK_WITH_NULL_SHA:
+			return "PSK_WITH_NULL_SHA";
+		case TLS_DHE_PSK_WITH_NULL_SHA:
+			return "DHE_PSK_WITH_NULL_SHA";
+		case TLS_RSA_PSK_WITH_NULL_SHA:
+			return "RSA_PSK_WITH_NULL_SHA";
+
+		/*
+		 * 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";
+		case TLS_RSA_WITH_AES_256_GCM_SHA384:
+			return "RSA_WITH_AES_256_GCM_SHA384";
+		case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
+			return "DHE_RSA_WITH_AES_128_GCM_SHA256";
+		case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
+			return "DHE_RSA_WITH_AES_256_GCM_SHA384";
+		case TLS_DH_RSA_WITH_AES_128_GCM_SHA256:
+			return "DH_RSA_WITH_AES_128_GCM_SHA256";
+		case TLS_DH_RSA_WITH_AES_256_GCM_SHA384:
+			return "DH_RSA_WITH_AES_256_GCM_SHA384";
+		case TLS_DHE_DSS_WITH_AES_128_GCM_SHA256:
+			return "DHE_DSS_WITH_AES_128_GCM_SHA256";
+		case TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
+			return "DHE_DSS_WITH_AES_256_GCM_SHA384";
+		case TLS_DH_DSS_WITH_AES_128_GCM_SHA256:
+			return "DH_DSS_WITH_AES_128_GCM_SHA256";
+		case TLS_DH_DSS_WITH_AES_256_GCM_SHA384:
+			return "DH_DSS_WITH_AES_256_GCM_SHA384";
+		case TLS_DH_anon_WITH_AES_128_GCM_SHA256:
+			return "DH_anon_WITH_AES_128_GCM_SHA256";
+		case TLS_DH_anon_WITH_AES_256_GCM_SHA384:
+			return "DH_anon_WITH_AES_256_GCM_SHA384";
+
+		/* 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";
+		case TLS_PSK_WITH_AES_256_GCM_SHA384:
+			return "PSK_WITH_AES_256_GCM_SHA384";
+		case TLS_DHE_PSK_WITH_AES_128_GCM_SHA256:
+			return "DHE_PSK_WITH_AES_128_GCM_SHA256";
+		case TLS_DHE_PSK_WITH_AES_256_GCM_SHA384:
+			return "DHE_PSK_WITH_AES_256_GCM_SHA384";
+		case TLS_RSA_PSK_WITH_AES_128_GCM_SHA256:
+			return "RSA_PSK_WITH_AES_128_GCM_SHA256";
+		case TLS_RSA_PSK_WITH_AES_256_GCM_SHA384:
+			return "RSA_PSK_WITH_AES_256_GCM_SHA384";
+		case TLS_PSK_WITH_AES_128_CBC_SHA256:
+			return "PSK_WITH_AES_128_CBC_SHA256";
+		case TLS_PSK_WITH_AES_256_CBC_SHA384:
+			return "PSK_WITH_AES_256_CBC_SHA384";
+		case TLS_PSK_WITH_NULL_SHA256:
+			return "PSK_WITH_NULL_SHA256";
+		case TLS_PSK_WITH_NULL_SHA384:
+			return "PSK_WITH_NULL_SHA384";
+		case TLS_DHE_PSK_WITH_AES_128_CBC_SHA256:
+			return "DHE_PSK_WITH_AES_128_CBC_SHA256";
+		case TLS_DHE_PSK_WITH_AES_256_CBC_SHA384:
+			return "DHE_PSK_WITH_AES_256_CBC_SHA384";
+		case TLS_DHE_PSK_WITH_NULL_SHA256:
+			return "DHE_PSK_WITH_NULL_SHA256";
+		case TLS_DHE_PSK_WITH_NULL_SHA384:
+			return "DHE_PSK_WITH_NULL_SHA384";
+		case TLS_RSA_PSK_WITH_AES_128_CBC_SHA256:
+			return "RSA_PSK_WITH_AES_128_CBC_SHA256";
+		case TLS_RSA_PSK_WITH_AES_256_CBC_SHA384:
+			return "RSA_PSK_WITH_AES_256_CBC_SHA384";
+		case TLS_RSA_PSK_WITH_NULL_SHA256:
+			return "RSA_PSK_WITH_NULL_SHA256";
+		case TLS_RSA_PSK_WITH_NULL_SHA384:
+			return "RSA_PSK_WITH_NULL_SHA384";
+
+		/*
+		 * 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";
+		case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
+			return "ECDHE_ECDSA_WITH_AES_256_CBC_SHA384";
+		case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
+			return "ECDH_ECDSA_WITH_AES_128_CBC_SHA256";
+		case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
+			return "ECDH_ECDSA_WITH_AES_256_CBC_SHA384";
+		case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+			return "ECDHE_RSA_WITH_AES_128_CBC_SHA256";
+		case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
+			return "ECDHE_RSA_WITH_AES_256_CBC_SHA384";
+		case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
+			return "ECDH_RSA_WITH_AES_128_CBC_SHA256";
+		case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
+			return "ECDH_RSA_WITH_AES_256_CBC_SHA384";
+
+		/*
+		 * 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";
+		case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+			return "ECDHE_ECDSA_WITH_AES_256_GCM_SHA384";
+		case TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
+			return "ECDH_ECDSA_WITH_AES_128_GCM_SHA256";
+		case TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384:
+			return "ECDH_ECDSA_WITH_AES_256_GCM_SHA384";
+		case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+			return "ECDHE_RSA_WITH_AES_128_GCM_SHA256";
+		case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+			return "ECDHE_RSA_WITH_AES_256_GCM_SHA384";
+		case TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
+			return "ECDH_RSA_WITH_AES_128_GCM_SHA256";
+		case TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
+			return "ECDH_RSA_WITH_AES_256_GCM_SHA384";
+
+		default:
+			break;
+	}
+
+	return NULL;
+}
+
+/*
+ * pg_SSLcipherbits
+ *
+ * Return the number of bits in the encryption for the specified cipher.
+ * Ciphers with NULL encryption are omitted from the switch statement.
+ */
+static int
+pg_SSLcipherbits(SSLCipherSuite cipher)
+{
+	switch (cipher)
+	{
+		/* TLS addenda using AES, per RFC 3268 */
+		case TLS_RSA_WITH_AES_128_CBC_SHA:
+		case TLS_DH_DSS_WITH_AES_128_CBC_SHA:
+		case TLS_DH_RSA_WITH_AES_128_CBC_SHA:
+		case TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
+		case TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+		case TLS_DH_anon_WITH_AES_128_CBC_SHA:
+			return 128;
+
+		case TLS_RSA_WITH_AES_256_CBC_SHA:
+		case TLS_DH_DSS_WITH_AES_256_CBC_SHA:
+		case TLS_DH_RSA_WITH_AES_256_CBC_SHA:
+		case TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
+		case TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+		case TLS_DH_anon_WITH_AES_256_CBC_SHA:
+			return 256;
+
+		/* ECDSA addenda, RFC 4492 */
+		case TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
+		case TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA:
+		case TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
+		case TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
+		case TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
+			return 112;
+
+		case TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
+		case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
+		case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
+		case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
+		case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+		case TLS_ECDH_anon_WITH_AES_128_CBC_SHA:
+		case TLS_ECDH_anon_WITH_RC4_128_SHA:
+		case TLS_ECDHE_RSA_WITH_RC4_128_SHA:
+		case TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
+		case TLS_ECDH_RSA_WITH_RC4_128_SHA:
+			return 128;
+
+		case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+		case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
+		case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
+		case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+		case TLS_ECDH_anon_WITH_AES_256_CBC_SHA:
+			return 256;
+
+		/* Server provided RSA certificate for key exchange. */
+		case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+			return 112;
+		case TLS_RSA_WITH_RC4_128_MD5:
+		case TLS_RSA_WITH_RC4_128_SHA:
+		case TLS_RSA_WITH_AES_128_CBC_SHA256:
+			return 128;
+		case TLS_RSA_WITH_AES_256_CBC_SHA256:
+			return 256;
+
+		/*
+		 * Server-authenticated (and optionally client-authenticated)
+		 * Diffie-Hellman.
+		 */
+		case TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
+		case TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA:
+		case TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
+		case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+			return 112;
+		case TLS_DH_DSS_WITH_AES_128_CBC_SHA256:
+		case TLS_DH_RSA_WITH_AES_128_CBC_SHA256:
+		case TLS_DHE_DSS_WITH_AES_128_CBC_SHA256:
+		case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+			return 128;
+		case TLS_DH_DSS_WITH_AES_256_CBC_SHA256:
+		case TLS_DH_RSA_WITH_AES_256_CBC_SHA256:
+		case TLS_DHE_DSS_WITH_AES_256_CBC_SHA256:
+		case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+			return 256;
+
+		/* Completely anonymous Diffie-Hellman */
+		case TLS_DH_anon_WITH_3DES_EDE_CBC_SHA:
+			return 112;
+		case TLS_DH_anon_WITH_RC4_128_MD5:
+		case TLS_DH_anon_WITH_AES_128_CBC_SHA256:
+			return 128;
+		case TLS_DH_anon_WITH_AES_256_CBC_SHA256:
+			return 256;
+
+		/* Addendum from RFC 4279, TLS PSK */
+		case TLS_PSK_WITH_3DES_EDE_CBC_SHA:
+		case TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA:
+		case TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
+			return 112;
+		case TLS_PSK_WITH_RC4_128_SHA:
+		case TLS_DHE_PSK_WITH_RC4_128_SHA:
+		case TLS_PSK_WITH_AES_128_CBC_SHA:
+		case TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
+		case TLS_RSA_PSK_WITH_RC4_128_SHA:
+		case TLS_RSA_PSK_WITH_AES_128_CBC_SHA:
+			return 128;
+		case TLS_PSK_WITH_AES_256_CBC_SHA:
+		case TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
+		case TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
+			return 256;
+
+		/*
+		 * Addenda from RFC 5288, AES Galois Counter Mode (GCM) Cipher Suites
+		 * for TLS.
+		 */
+		case TLS_RSA_WITH_AES_128_GCM_SHA256:
+		case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
+		case TLS_DH_RSA_WITH_AES_128_GCM_SHA256:
+		case TLS_DHE_DSS_WITH_AES_128_GCM_SHA256:
+		case TLS_DH_DSS_WITH_AES_128_GCM_SHA256:
+		case TLS_DH_anon_WITH_AES_128_GCM_SHA256:
+			return 128;
+
+		case TLS_RSA_WITH_AES_256_GCM_SHA384:
+		case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
+		case TLS_DH_RSA_WITH_AES_256_GCM_SHA384:
+		case TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
+		case TLS_DH_DSS_WITH_AES_256_GCM_SHA384:
+		case TLS_DH_anon_WITH_AES_256_GCM_SHA384:
+			return 256;
+
+		/* RFC 5487 - PSK with SHA-256/384 and AES GCM */
+
+
+		case TLS_PSK_WITH_AES_128_GCM_SHA256:
+		case TLS_DHE_PSK_WITH_AES_128_GCM_SHA256:
+		case TLS_RSA_PSK_WITH_AES_128_GCM_SHA256:
+		case TLS_PSK_WITH_AES_128_CBC_SHA256:
+		case TLS_DHE_PSK_WITH_AES_128_CBC_SHA256:
+		case TLS_RSA_PSK_WITH_AES_128_CBC_SHA256:
+			return 128;
+		case TLS_DHE_PSK_WITH_AES_256_GCM_SHA384:
+		case TLS_PSK_WITH_AES_256_GCM_SHA384:
+		case TLS_RSA_PSK_WITH_AES_256_GCM_SHA384:
+		case TLS_PSK_WITH_AES_256_CBC_SHA384:
+		case TLS_DHE_PSK_WITH_AES_256_CBC_SHA384:
+		case TLS_RSA_PSK_WITH_AES_256_CBC_SHA384:
+			return 256;
+
+		/*
+		 * Addenda from RFC 5289, Elliptic Curve Cipher Suites with
+		 * HMAC SHA-256/384.
+		 */
+		case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+		case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
+		case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+		case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
+			return 128;
+		case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
+		case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
+		case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
+		case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
+			return 256;
+
+		/*
+		 * 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:
+		case TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
+		case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+		case TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
+			return 128;
+		case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+		case TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384:
+		case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+		case TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
+			return 256;
+
+		default:
+			break;
+	}
+
+	return 0;
+}
+#endif							/* SECURETRANSPORT_H */
diff --git a/src/include/common/sha2.h b/src/include/common/sha2.h
index a31b3979d8..2851d3eea0 100644
--- a/src/include/common/sha2.h
+++ b/src/include/common/sha2.h
@@ -50,7 +50,7 @@
 #ifndef _PG_SHA2_H_
 #define _PG_SHA2_H_
 
-#ifdef USE_SSL
+#if defined(USE_SSL) && defined(USE_OPENSSL)
 #include <openssl/sha.h>
 #endif
 
@@ -69,7 +69,7 @@
 #define PG_SHA512_DIGEST_STRING_LENGTH	(PG_SHA512_DIGEST_LENGTH * 2 + 1)
 
 /* Context Structures for SHA-1/224/256/384/512 */
-#ifdef USE_SSL
+#if defined(USE_SSL) && defined(USE_OPENSSL)
 typedef SHA256_CTX pg_sha256_ctx;
 typedef SHA512_CTX pg_sha512_ctx;
 typedef SHA256_CTX pg_sha224_ctx;
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 7bde744d51..0f9ff6b88c 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -22,6 +22,14 @@
 #ifdef USE_OPENSSL
 #include <openssl/ssl.h>
 #include <openssl/err.h>
+#elif USE_SECURETRANSPORT
+/*
+ * Ideally we should include the Secure Transport headers here but doing so
+ * cause namespace collisions with CoreFoundation on, among others "Size"
+ * and ACL definitions. To avoid polluting with workarounds, use void * for
+ * instead of the actual Secure Transport variables and perform type casting
+ * in the Secure Transport implementation.
+ */
 #endif
 #ifdef HAVE_NETINET_TCP_H
 #include <netinet/tcp.h>
@@ -183,19 +191,22 @@ typedef struct Port
 	bool		peer_cert_valid;
 
 	/*
-	 * OpenSSL structures. (Keep these last so that the locations of other
-	 * fields are the same whether or not you build with OpenSSL.)
+	 * SSL library structures. (Keep these last so that the locations of
+	 * other fields are the same whether or not you build with SSL.)
 	 */
 #ifdef USE_OPENSSL
 	SSL		   *ssl;
 	X509	   *peer;
+#elif USE_SECURETRANSPORT
+	void	   *ssl;
+	int			ssl_buffered;
 #endif
 } Port;
 
 #ifdef USE_SSL
 /*
  * These functions are implemented by the glue code specific to each
- * SSL implementation (e.g. be-secure-openssl.c)
+ * SSL implementation (e.g. be-secure-<implementation>.c)
  */
 extern int	be_tls_init(bool isServerStart);
 extern void be_tls_destroy(void);
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 7a05c7e5b8..4051ec32cc 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -844,6 +844,9 @@
 /* Define to use OpenSSL for random number generation */
 #undef USE_OPENSSL_RANDOM
 
+/* 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 f3b35297d1..48cc363803 100644
--- a/src/include/pg_config_manual.h
+++ b/src/include/pg_config_manual.h
@@ -166,11 +166,15 @@
 
 /*
  * USE_SSL code should be compiled only when compiling with an SSL
- * implementation.  (Currently, only OpenSSL is supported, but we might add
- * more implementations in the future.)
+ * implementation.
  */
-#ifdef USE_OPENSSL
+#if defined(USE_OPENSSL) || defined(USE_SECURETRANSPORT)
 #define USE_SSL
+#if defined(USE_OPENSSL)
+#define SSL_LIBRARY "OpenSSL"
+#elif defined(USE_SECURETRANSPORT)
+#define SSL_LIBRARY "Secure Transport"
+#endif
 #endif
 
 /*
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index 87f22d242f..ad615420d6 100644
--- a/src/interfaces/libpq/Makefile
+++ b/src/interfaces/libpq/Makefile
@@ -57,6 +57,11 @@ else
 OBJS += sha2.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
@@ -112,7 +117,6 @@ ip.c md5.c base64.c scram-common.c sha2.c sha2_openssl.c saslprep.c unicode_norm
 encnames.c wchar.c: % : $(backend_src)/utils/mb/%
 	rm -f $@ && $(LN_S) $< .
 
-
 distprep: libpq-dist.rc
 
 libpq.rc libpq-dist.rc: libpq.rc.in
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 5a964bf0b5..f255ee5487 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -323,6 +323,12 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
 		"Target-Session-Attrs", "", 11, /* sizeof("read-write") = 11 */
 	offsetof(struct pg_conn, target_session_attrs)},
 
+#if defined(USE_SECURETRANSPORT)
+	{"keychain", "PGKEYCHAIN", NULL, NULL,
+		"Keychain", "", 64,
+	offsetof(struct pg_conn, keychain)},
+#endif
+
 	/* Terminating entry --- MUST BE LAST */
 	{NULL, NULL, NULL, NULL,
 	NULL, NULL, 0}
diff --git a/src/interfaces/libpq/fe-secure-securetransport.c b/src/interfaces/libpq/fe-secure-securetransport.c
new file mode 100644
index 0000000000..f5593b7ab2
--- /dev/null
+++ b/src/interfaces/libpq/fe-secure-securetransport.c
@@ -0,0 +1,1342 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-secure-securetransport.c
+ *	  Apple Secure Transport support
+ *
+ *
+ * Portions Copyright (c) 1996-2017, 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.
+ *
+ *	  There are a lot of functions (mostly the Core Foundation CF* family) that
+ *	  pass NULL as the first parameter. This is because they allow for a custom
+ *	  allocator to be used for memory allocations which is referenced with the
+ *	  first parameter. We are using the standard allocator however, and that
+ *	  means passing NULL all the time. Defining a suitably named preprocessor
+ *	  macro would aid readiblitity in case this is confusing (and/or ugly).
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#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>
+#include "common/securetransport.h"
+
+
+/*
+ * 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 * pg_SSLerrmessage(OSStatus errcode);
+static void pg_SSLerrfree(char *err_buf);
+static int pg_SSLsessionstate(PGconn *conn, char *msg, size_t len);
+
+static OSStatus pg_SSLSocketRead(SSLConnectionRef conn, void *data,
+							  size_t *len);
+static OSStatus pg_SSLSocketWrite(SSLConnectionRef conn, const void *data,
+							   size_t *len);
+static OSStatus pg_SSLOpenClient(PGconn *conn);
+static OSStatus pg_SSLLoadCertificate(PGconn *conn, CFArrayRef *cert_array,
+								   CFArrayRef *key_array,
+								   CFArrayRef *rootcert_array);
+
+static OSStatus import_certificate_keychain(const char *certificate,
+											SecIdentityRef *identity,
+											SecKeychainRef keychain);
+static OSStatus import_pem(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)
+{
+	return;
+}
+
+/*
+ *	Begin or continue negotiating a secure session.
+ */
+PostgresPollingStatusType
+pgtls_open_client(PGconn *conn)
+{
+	OSStatus		open_status;
+	CFArrayRef		certificate = NULL;
+	CFArrayRef		key = NULL;
+	CFArrayRef		rootcert = NULL;
+
+	/*
+	 * There is no API to load CRL lists in Secure Transport, they can however
+	 * be imported into a Keychain with the commandline application "certtool".
+	 * For libpq to use them, the certificate/key and root certificate needs to
+	 * be using an identity in a Keychain into which the CRL have been
+	 * imported. That needs to be documented.
+	 */
+	if (conn->sslcrl && strlen(conn->sslcrl) > 0)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+			   libpq_gettext("CRL files are not supported with Secure Transport\n"));
+		return PGRES_POLLING_FAILED;
+	}
+
+	/*
+	 * 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;
+		conn->st_rootcert = NULL;
+
+		conn->ssl = SSLCreateContext(NULL, kSSLClientSide, kSSLStreamType);
+		if (!conn->ssl)
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+				   libpq_gettext("could not create SSL context\n"));
+			return PGRES_POLLING_FAILED;
+		}
+
+		open_status = SSLSetProtocolVersionMin(conn->ssl, kTLSProtocol12);
+		if (open_status != noErr)
+			goto error;
+
+		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, pg_SSLSocketRead, pg_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 in case of error.
+		 */
+		if (pg_SSLLoadCertificate(conn, &certificate, &key, &rootcert) != noErr)
+		{
+			pgtls_close(conn);
+			return PGRES_POLLING_FAILED;
+		}
+
+		/*
+		 * 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);
+				printfPQExpBuffer(&conn->errorMessage,
+								  libpq_gettext("hostname missing for verify-full\n"));
+				return PGRES_POLLING_FAILED;
+			}
+
+			SSLSetPeerDomainName(conn->ssl, conn->pghost, strlen(conn->pghost));
+		}
+	}
+
+	/*
+	 * Perform handshake
+	 */
+	open_status = pg_SSLOpenClient(conn);
+	if (open_status == noErr)
+	{
+		conn->ssl_in_use = true;
+		return PGRES_POLLING_OK;
+	}
+
+error:
+	if (open_status != noErr)
+	{
+		char *err_msg = pg_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);
+		pg_SSLerrfree(err_msg);
+
+		pgtls_close(conn);
+	}
+
+	return PGRES_POLLING_FAILED;
+}
+
+/*
+ * pg_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
+pg_SSLOpenClient(PGconn *conn)
+{
+	OSStatus			status;
+	SecTrustRef			trust = NULL;
+	SecTrustResultType	trust_eval = 0;
+	bool				trusted = false;
+	bool				only_anchor = true;
+
+	SSLSetSessionOption(conn->ssl, kSSLSessionOptionBreakOnServerAuth, true);
+
+	/*
+	 * Call SSLHandshake until we get another response than errSSLWouldBlock.
+	 * Busy-waiting is pretty silly, but what is commonly used for handshakes
+	 * in Secure Transport. Setting an upper bound on retries should be done
+	 * though, and perhaps a small timeout to play nice.
+	 */
+	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 (trust == noErr ? errSecInternalError : 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,
+											   (CFArrayRef) 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 a valid anchor certificate was verified
+			 * 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. Both cases are
+			 * considered valid so trust the chain.
+			 */
+			case kSecTrustResultUnspecified:
+				trusted = true;
+				break;
+			case kSecTrustResultProceed:
+				trusted = true;
+				break;
+
+			/*
+			 * 'RecoverableTrustFailure' indicates that the certificate was
+			 * rejected but might be trusted with minor changes to the eval
+			 * context (ignoring expired certificate etc). For the verify
+			 * sslmodes there is little to do here, but in require sslmode we
+			 * can recover in some cases.
+			 */
+			case kSecTrustResultRecoverableTrustFailure:
+			{
+				CFArrayRef 			trust_prop;
+				CFDictionaryRef		trust_dict;
+				CFStringRef			trust_error;
+				const char		   *error;
+
+				/* Assume the error is in fact not recoverable */
+				trusted = false;
+
+				/*
+				 * In sslmode "require" we accept some certificate verification
+				 * failures when we don't have a rootcert since MITM protection
+				 * isn't enforced. Check the reported failure and trust in case
+				 * the cert is missing, self signed or expired/future.
+				 */
+				if (strcmp(conn->sslmode, "require") == 0 && !conn->st_rootcert)
+				{
+					trust_prop = SecTrustCopyProperties(trust);
+					trust_dict = CFArrayGetValueAtIndex(trust_prop, 0);
+					trust_error = CFDictionaryGetValue(trust_dict,
+													   kSecPropertyTypeError);
+					if (trust_error)
+					{
+						error = CFStringGetCStringPtr(trust_error,
+													  kCFStringEncodingUTF8);
+
+						/* Self signed, or missing CA */
+						if (strcmp(error, "CSSMERR_TP_INVALID_ANCHOR_CERT") == 0 ||
+							strcmp(error, "CSSMERR_TP_NOT_TRUSTED") == 0 ||
+							strcmp(error, "CSSMERR_TP_INVALID_CERT_AUTHORITY") == 0)
+							trusted = true;
+						/* Expired or future dated */
+						else if (strcmp(error, "CSSMERR_TP_CERT_EXPIRED") == 0 ||
+								 strcmp(error, "CSSMERR_TP_CERT_NOT_VALID_YET") == 0)
+							trusted = true;
+					}
+
+					CFRelease(trust_prop);
+				}
+
+				break;
+			}
+
+			/*
+			 * The below results are all cases where the certificate should be
+			 * rejected without further questioning.
+			 */
+
+			/*
+			 * 'Deny' means that the user has explicitly set the certificate to
+			 * untrusted.
+			 */
+			case kSecTrustResultDeny:
+				/* fall-through */
+			case kSecTrustResultInvalid:
+				/* fall-through */
+			case kSecTrustResultFatalTrustFailure:
+				/* fall-through */
+			case kSecTrustResultOtherError:
+				/* fall-through */
+			default:
+				trusted = false;
+				break;
+		}
+	}
+
+	CFRelease(trust);
+
+	/*
+	 * 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 pg_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 decide
+ * whether to continue or 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 (pg_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 errSSLWouldBlock:
+			/* Only set read_errno to EINTR iff we didn't get any data back */
+			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 decide
+ * whether to continue or 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 (pg_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
+		{
+			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 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;
+
+	if (conn->st_rootcert != NULL)
+		CFRelease((CFArrayRef) 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
+pg_SSLSocketRead(SSLConnectionRef conn, void *data, size_t *len)
+{
+	OSStatus	status = noErr;
+	int			res;
+
+	res = pqsecure_raw_read((PGconn *) conn, data, *len);
+
+	if (res < 0)
+	{
+		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 = 0;
+	}
+	else
+		*len = res;
+
+	return status;
+}
+
+static OSStatus
+pg_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)
+	{
+		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;
+}
+
+/*
+ * import_certificate_keychain
+ *
+ * Queries the specified Keychain, or the default unless not defined, for a
+ * certificate with the passed identity.  Keychains are searched by creating a
+ * dictionary of key/value pairs with the search criteria and then asking for a
+ * copy of the matching entry/entries to the search criteria.
+ */
+static OSStatus
+import_certificate_keychain(const char *certificate, SecIdentityRef *identity,
+							SecKeychainRef keychain)
+{
+	OSStatus				status = errSecItemNotFound;
+	CFMutableDictionaryRef	query;
+	CFStringRef				cert;
+	CFArrayRef				temp;
+
+	query = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks,
+									  &kCFTypeDictionaryValueCallBacks);
+
+	cert = CFStringCreateWithCString(NULL, certificate, kCFStringEncodingUTF8);
+
+	/*
+	 * If we didn't get a Keychain passed, skip adding it to the dictionary
+	 * thus prompting a search in the users default Keychain.
+	 */
+	if (keychain)
+		CFDictionaryAddValue(query, kSecUseKeychain, keychain);
+	
+	CFDictionaryAddValue(query, kSecClass, kSecClassIdentity);
+	CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue);
+	CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitAll);
+	CFDictionaryAddValue(query, kSecMatchPolicy, SecPolicyCreateSSL(false, NULL));
+	CFDictionaryAddValue(query, kSecAttrLabel, cert);
+
+	/*
+	 * Normally we could have used kSecMatchLimitOne in the above dictionary
+	 * but since there are versions of macOS where the certificate matching on
+	 * the label doesn't work, we need to request all and find the one we want.
+	 * Copy all the results to a temp array and scan it for the certificate we
+	 * are interested in.
+	 */
+	status = SecItemCopyMatching(query, (CFTypeRef *) &temp);
+	if (status == noErr)
+	{
+		OSStatus	search_stat;
+		SecIdentityRef dummy;
+		int			i;
+
+		for (i = 0; i < CFArrayGetCount(temp); i++)
+		{
+			SecCertificateRef	search_cert;
+			CFStringRef			cn;
+
+			dummy = (SecIdentityRef) CFArrayGetValueAtIndex(temp, i);
+			search_stat = SecIdentityCopyCertificate(dummy, &search_cert);
+
+			if (search_stat == noErr)
+			{
+				SecCertificateCopyCommonName(search_cert, &cn);
+				if (CFStringCompare(cn, cert, 0) == kCFCompareEqualTo)
+				{
+					CFRelease(cn);
+					CFRelease(search_cert);
+					*identity = (SecIdentityRef) CFRetain(dummy);
+					break;
+				}
+
+				CFRelease(cn);
+				CFRelease(search_cert);
+			}
+		}
+
+		CFRelease(temp);
+	}
+
+	CFRelease(query);
+	CFRelease(cert);
+
+	return status;
+}
+
+static OSStatus
+import_pem(const char *path, int size, char *passphrase, CFArrayRef *certificate)
+{
+	OSStatus							status;
+	CFDataRef							data_ref;
+	CFStringRef							file_type;
+	SecExternalItemType					item_type;
+	SecItemImportExportKeyParameters	params;
+	SecExternalFormat					format;
+	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;
+	}
+
+	if (fread(certdata, 1, size, fp) != size)
+	{
+		fclose(fp);
+		return errSecInternalError;
+	}
+	fclose(fp);
+
+	data_ref = CFDataCreate(NULL, 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);
+
+	/*
+	 * Though we explicitly say this is a PEM file, Secure Transport will
+	 * consider that a mere hint. Providing a file ending and a file format is
+	 * what we can do to assist.
+	 */
+	file_type = CFSTR(".pem");
+	if (!file_type)
+		return errSecInternalError;
+
+	format = kSecFormatPEMSequence;
+	item_type = kSecItemTypeCertificate;
+
+	status = SecItemImport(data_ref, file_type, &format, &item_type,
+						   0 /* flags */, &params, NULL /* keychain */,
+						   certificate);
+
+	return status;
+}
+
+/*
+ * Secure Transport has the concept of an identity, which is a packaging of a
+ * private key and the certificate which contains the public key. The identity
+ * is what is used for verifying the connection, so we need to provide a
+ * SecIdentityRef identity to the API.
+ *
+ * A complete, and packaged, identity can be contained in a Keychain, in which
+ * case we can load it directly without having to create anything ourselves.
+ * In the case where we don't have a prepared identity in a Keychain, we need
+ * to create it from its components (certificate and key). The certificate must
+ * in that case be be located in a PEM file on the local filesystem. The key
+ * can either be in a PEM file or in the Keychain.
+ *
+ * While keeping identities in the Keychain is the macOS thing to do, we want
+ * to be functionally compatible with the OpenSSL support in libpq. Thus we not
+ * only need to support creating an identity from a private key contained in a
+ * PEM file, it needs to be the default.  The order in which we discover the
+ * identity is:
+ *
+ * 1. Certificate and key in local files
+ * 2. Certificate in local file and key in Keychain
+ * 3. Identity in Keychain
+ *
+ * Since failures can come from multiple places, the PGconn errorMessage is
+ * populated here even for SSL library errors.
+ */
+static OSStatus
+pg_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 = false;
+	char	   		   *ssl_err_msg;
+	SecIdentityRef		identity = NULL;
+	SecCertificateRef	cert_ref;
+	SecKeyRef			key_ref = NULL;
+	CFArrayRef			keychains = NULL;
+
+	/*
+	 * If we have a keychain configured, open the referenced keychain as well
+	 * as the default keychain and prepare an array with the references for
+	 * searching.
+	 */
+	if (conn->keychain)
+	{
+		SecKeychainRef kcref[2];
+
+		if (stat(conn->keychain, &buf) == 0)
+		{
+			status = SecKeychainOpen(conn->keychain, &kcref[0]);
+			if (status == noErr)
+			{
+				SecKeychainCopyDefault(&kcref[1]);
+				keychains = CFArrayCreate(NULL, (const void **) kcref, 2,
+										  &kCFTypeArrayCallBacks);
+			}
+		}
+	}
+
+	/*
+	 * 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))
+		have_homedir = pqGetHomeDirectory(homedir, sizeof(homedir));
+	else	/* won't need it */
+		have_homedir = false;
+
+	/*
+	 * Prepare the filename for reading the certificate/ A configured sslcert
+	 * has priority
+	 */
+	if (conn->sslcert && strlen(conn->sslcert) > 0)
+		strlcpy(fnbuf, conn->sslcert, sizeof(fnbuf));
+	/* If there is a keychain explicitly configured it trumps defaults */
+	else if (have_homedir && !conn->keychain)
+		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_CERT_FILE);
+	else
+		fnbuf[0] = '\0';
+
+	/*
+	 * If there is a file matching the path, the certificate must be loaded
+	 * from this file for us to continue.
+	 */
+	if (fnbuf[0] != '\0' && stat(fnbuf, &buf) == 0)
+	{
+		status = import_pem(fnbuf, buf.st_size, NULL, cert_array);
+		if (status != noErr)
+		{
+			ssl_err_msg = pg_SSLerrmessage(status);
+			printfPQExpBuffer(&conn->errorMessage,
+				libpq_gettext("could not load certificate file \"%s\": %s\n"),
+							  fnbuf, ssl_err_msg);
+			pg_SSLerrfree(ssl_err_msg);
+			return status;
+		}
+
+		cert_ref = (SecCertificateRef) CFArrayGetValueAtIndex(*cert_array, 0);
+		have_cert = true;
+
+		/*
+		 * We now have a certificate, so we need a private key as well in order
+		 * to create the identity. There are two places where we can find it:
+		 * in a file on the filesystem or in a Keychain. If the sslkey config
+		 * isn't set it can mean two things, either the user wants the default
+		 * or the key is in a Keychain in which case there is sslkey name to
+		 * reference since the key is located by the certificate. At this point
+		 * we don't know which, so we look for the default file first and fall
+		 * back to a Keychain lookup.
+		 *
+		 * First we look for the presence of a file.
+		 */
+		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
+			fnbuf[0] = '\0';
+
+		/*
+		 * If there is a matching file on the filesystem, require the key to be
+		 * loaded from that file.
+		 */
+		if (fnbuf[0] != '\0' && stat(fnbuf, &buf) == 0)
+		{
+			status = import_pem(fnbuf, buf.st_size, NULL, key_array);
+			if (status != noErr)
+			{
+				ssl_err_msg = pg_SSLerrmessage(status);
+				printfPQExpBuffer(&conn->errorMessage,
+					libpq_gettext("could not load private key file \"%s\": %s\n"),
+								  fnbuf, ssl_err_msg);
+				pg_SSLerrfree(ssl_err_msg);
+				return status;
+			}
+
+			key_ref = (SecKeyRef) CFArrayGetValueAtIndex(*key_array, 0);
+
+			/*
+			 * We have certificate and a key loaded from files on disk, now we
+			 * can create an identity from this pair.
+			 */
+			identity = SecIdentityCreate(NULL, cert_ref, key_ref);
+		}
+		/*
+		 * There is no matching file, so we attempt to load the key from a
+		 * Keychain by referencing the certificate.
+		 */
+		else
+		{
+			/*
+			 * Search for the private key matching the cert_ref in the default
+			 * Keychain. If found, we get the identity returned.
+			 */
+			status = SecIdentityCreateWithCertificate(keychains, cert_ref, &identity);
+			if (status != noErr)
+			{
+				printfPQExpBuffer(&conn->errorMessage,
+					libpq_gettext("certificate present, but private key \"%s\" not found on disk or in Keychain\n"),
+								  fnbuf);
+				return errSecInternalError;
+			}
+
+			SecIdentityCopyPrivateKey(identity, &key_ref);
+		}
+	}
+	/*
+	 * If the certificate file is not present, it might be a reference to an
+	 * identity in a Keychain instead so don't treat that as an error. Any
+	 * other error, however, is grounds for complaint.
+	 */
+	else
+	{
+		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;
+		}
+	}
+
+	if (!identity)
+	{
+		/*
+		 * The certificate is not located in a file, this means that we must
+		 * look for the complete identity in a Keychain instead.
+		 */
+		if (keychains)
+		{
+			import_certificate_keychain(conn->sslcert ? conn->sslcert : USER_CERT_FILE, &identity,
+										(SecKeychainRef) CFArrayGetValueAtIndex(keychains, 0));
+			if (!identity)
+				import_certificate_keychain(conn->sslcert ? conn->sslcert : USER_CERT_FILE, &identity,
+											(SecKeychainRef) CFArrayGetValueAtIndex(keychains, 1));
+		}
+		else
+			import_certificate_keychain(conn->sslcert ? conn->sslcert : USER_CERT_FILE, &identity, NULL);
+
+		if (identity)
+			SecIdentityCopyPrivateKey(identity, &key_ref);
+	}
+
+	if (identity)
+	{
+		CFMutableArrayRef cert_connection;
+
+		/*
+		 * If we have created the identity "by hand" without involving the
+		 * Keychain we need to include the certificates in the array passed to
+		 * SSLSetCertificate()
+		 */
+		if (have_cert)
+		{
+			cert_connection = CFArrayCreateMutableCopy(NULL, 0, *cert_array);
+			CFArraySetValueAtIndex(cert_connection, 0, identity);
+		}
+		else
+		{
+			cert_connection = CFArrayCreateMutable(NULL, 1L,
+												   &kCFTypeArrayCallBacks);
+			CFArrayInsertValueAtIndex(cert_connection, 0,
+									  (const void *) identity);
+		}
+
+		status = SSLSetCertificate(conn->ssl, cert_connection);
+
+		if (status != noErr)
+		{
+			ssl_err_msg = pg_SSLerrmessage(status);
+			printfPQExpBuffer(&conn->errorMessage,
+					libpq_gettext("could not set certificate for connection: (%d) %s\n"),
+								  status, ssl_err_msg);
+			pg_SSLerrfree(ssl_err_msg);
+			return status;
+		}
+
+		if (key_ref)
+		{
+			conn->ssl_key_bits = SecKeyGetBlockSize(key_ref);
+			CFRelease(key_ref);
+		}
+	}
+
+	/* Try to 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 = pg_SSLerrmessage(status);
+				printfPQExpBuffer(&conn->errorMessage,
+						libpq_gettext("could not load root certificate file \"%s\": %s\n"),
+									  fnbuf, ssl_err_msg);
+				pg_SSLerrfree(ssl_err_msg);
+				return status;
+			}
+
+			if (*rootcert_array != NULL)
+				conn->st_rootcert = (void *) CFRetain(*rootcert_array);
+		}
+	}
+
+	return noErr;
+}
+
+/* ------------------------------------------------------------ */
+/*					SSL information functions					*/
+/* ------------------------------------------------------------ */
+
+int
+PQsslInUse(PGconn *conn)
+{
+	if (!conn)
+		return 0;
+	return conn->ssl_in_use;
+}
+
+void *
+PQgetssl(PGconn *conn)
+{
+	/*
+	 * Always return NULL as this is legacy and defined to be equal to
+	 * PQsslStruct(conn, "OpenSSL");
+	 */
+	return NULL;
+}
+
+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 pg_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 *
+pg_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
+pg_SSLerrfree(char *err_buf)
+{
+	if (err_buf && err_buf != ssl_nomem && err_buf != ssl_noerr)
+		free(err_buf);
+}
+
+/*
+ * pg_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 - 1 characters ensuring a NUL terminated returned string.
+ */
+static int
+pg_SSLsessionstate(PGconn *conn, char *msg, size_t len)
+{
+	SSLSessionState		state;
+	OSStatus			status;
+	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);
+	else
+	{
+		status = errSecInternalError;
+		state = -1;
+	}
+
+	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);
+}
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 42913604e3..0cf96c1f04 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -80,6 +80,14 @@ typedef struct
 #endif
 #endif							/* USE_OPENSSL */
 
+#ifdef USE_SECURETRANSPORT
+#define Size pg_Size
+#define uint64 pg_uint64
+#include <Security/Security.h>
+#undef Size
+#undef uint64
+#endif
+
 /*
  * POSTGRES backend dependent Constants.
  */
@@ -467,8 +475,18 @@ struct pg_conn
 	void	   *engine;			/* dummy field to keep struct the same if
 								 * OpenSSL version changes */
 #endif
-#endif							/* USE_OPENSSL */
-#endif							/* USE_SSL */
+#endif   /* USE_OPENSSL */
+
+#ifdef USE_SECURETRANSPORT
+	char	   *keychain;
+
+	SSLContextRef	 ssl;		/* SSL context reference */
+	void		    *st_rootcert;
+	ssize_t			 ssl_buffered;
+	int				 ssl_key_bits;
+#endif   /* USE_SECURETRANSPORT */
+
+#endif   /* USE_SSL */
 
 #ifdef ENABLE_GSS
 	gss_ctx_id_t gctx;			/* GSS context */
-- 
2.13.0.rc0.45.ge2cb6ab.dirty

