On 08/06/2014 08:37 PM, Jeff Janes wrote:
But now it looks like 0002 needs a rebase....

I've committed the refactoring patch, and here's a rebased and improved version of the Windows SChannel implementation over that.

Server-side support is now implemented too, but it's all very crude and work-in-progress. CRLs are not supported, intermediary CAs are not supported, and probably many other bells and whistles are missing too. But the basics work, including cert authentication. Consider this a Proof of Concept.

One issue came up with managing private keys: In the server, it's necessary to import the private key into a permanent key container that's managed by the Windows Crypto API. That can be done programmatically (as I do in the patch), but the keys are permanently stored in the system (in the user's profile). They will be left behind even if you completely delete the data directory. That's not the end of the world, but it would be nicer if we could use some kind of a temporary key container that only lives in memory, but the Crypto API doesn't seem to have such a concept. You can acquire an ephemeral context by passing the CRYPT_VERIFYCONTEXT flag to CryptAcquireContext function, and that's exactly what I'm doing in the client, but that method doesn't seem to work when acting as an SSL server.

Also, the key container needs to be given a name, or we can use the default container, but either way all the keys are shared among all applications that use the same container. We'll have to figure out how to set that up so that there are no conflicts, if you try to use the same server certificate for two PostgreSQL instances running on the same host (useful while developing/testing replication).

This isn't a showstopper, but needs some thought. As the patch stands, it uses a single key container called "PostgreSQL server key container", and makes no attempt to delete the keys after they're no longer used. That works, but it leaves the key lying on the system.

- Heikki

diff --git a/configure b/configure
index 0f435b5..d00290a 100755
--- a/configure
+++ b/configure
@@ -707,6 +707,7 @@ XML2_CONFIG
 UUID_EXTRA_OBJS
 with_uuid
 with_selinux
+with_winschannel
 with_openssl
 krb_srvtab
 with_python
@@ -823,6 +824,7 @@ with_pam
 with_ldap
 with_bonjour
 with_openssl
+with_winschannel
 with_selinux
 with_readline
 with_libedit_preferred
@@ -1509,6 +1511,7 @@ Optional Packages:
   --with-ldap             build with LDAP support
   --with-bonjour          build with Bonjour support
   --with-openssl          build with OpenSSL support
+  --with-winschannel      build with Windows SChannel support
   --with-selinux          build with SELinux support
   --without-readline      do not use GNU Readline nor BSD Libedit for editing
   --with-libedit-preferred
@@ -5514,6 +5517,46 @@ $as_echo "$with_openssl" >&6; }
 
 
 #
+# Windows SChannel
+#
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build with native Windows SSL support" >&5
+$as_echo_n "checking whether to build with native Windows SSL support... " >&6; }
+
+
+
+# Check whether --with-winschannel was given.
+if test "${with_winschannel+set}" = set; then :
+  withval=$with_winschannel;
+  case $withval in
+    yes)
+
+$as_echo "#define USE_WINDOWS_SCHANNEL 1" >>confdefs.h
+
+      ;;
+    no)
+      :
+      ;;
+    *)
+      as_fn_error $? "no argument expected for --with-winschannel option" "$LINENO" 5
+      ;;
+  esac
+
+else
+  with_winschannel=no
+
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_winschannel" >&5
+$as_echo "$with_winschannel" >&6; }
+
+
+if test "$with_openssl" = yes -a "$with_winschannel" = yes ; then
+  as_fn_error $? "
+*** Cannot select both OpenSSL and Windows SChannel." "$LINENO" 5
+fi
+
+#
 # SELinux
 #
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build with SELinux support" >&5
diff --git a/configure.in b/configure.in
index f8a4507..132fb0a 100644
--- a/configure.in
+++ b/configure.in
@@ -662,6 +662,20 @@ AC_MSG_RESULT([$with_openssl])
 AC_SUBST(with_openssl)
 
 #
+# Windows SChannel
+#
+AC_MSG_CHECKING([whether to build with native Windows SSL support])
+PGAC_ARG_BOOL(with, winschannel, no, [build with Windows SChannel support],
+              [AC_DEFINE([USE_WINDOWS_SCHANNEL], 1, [Define to build with Windows SChannel support. (--with-winschannel)])])
+AC_MSG_RESULT([$with_winschannel])
+AC_SUBST(with_winschannel)
+
+if test "$with_openssl" = yes -a "$with_winschannel" = yes ; then
+  AC_MSG_ERROR([
+*** Cannot select both OpenSSL and Windows SChannel.])
+fi
+
+#
 # SELinux
 #
 AC_MSG_CHECKING([whether to build with SELinux support])
diff --git a/src/backend/libpq/Makefile b/src/backend/libpq/Makefile
index 8be0572..88d0b8b 100644
--- a/src/backend/libpq/Makefile
+++ b/src/backend/libpq/Makefile
@@ -21,4 +21,8 @@ ifeq ($(with_openssl),yes)
 OBJS += be-secure-openssl.o
 endif
 
+ifeq ($(with_winschannel),yes)
+OBJS += be-secure-winschannel.o
+endif
+
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index e3a284b..651162e 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -922,10 +922,6 @@ aloop:
 		port->peer_cert_valid = true;
 	}
 
-	ereport(DEBUG2,
-			(errmsg("SSL connection from \"%s\"",
-					port->peer_cn ? port->peer_cn : "(anonymous)")));
-
 	/* set up debugging/info callback */
 	SSL_CTX_set_info_callback(SSL_context, info_cb);
 
diff --git a/src/backend/libpq/be-secure-winschannel.c b/src/backend/libpq/be-secure-winschannel.c
new file mode 100644
index 0000000..306f265
--- /dev/null
+++ b/src/backend/libpq/be-secure-winschannel.c
@@ -0,0 +1,1098 @@
+/*-------------------------------------------------------------------------
+ *
+ * be-secure-winschannel.c
+ *	  SSL support using Windows SChannel APIs
+ *
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/libpq/be-secure-winschannel.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "libpq/libpq-be.h"
+#include "libpq/libpq.h"
+#include "miscadmin.h"
+#include "storage/fd.h"
+
+#include <sspi.h>
+#include <schnlsp.h>
+
+#include <wincrypt.h>
+
+/*
+ * Name of the Crypto API key container to use for storing the server
+ * certificate and private key. 
+ */
+#define PG_KEY_CONTAINER_NAME L"PostgreSQL server key container"
+
+#define SCHANNEL_DEBUG LOG
+
+struct SchannelContext
+{
+	/* SChannel handle for the connection */
+	CtxtHandle  ctxHandle;
+	bool		ctxHandle_valid;
+	CredHandle	credHandle;
+	bool		credHandle_valid;
+
+	bool		remote_shutdown;
+
+	/*
+	 * Buffer for encrypting data to be written.
+	 *
+	 * When encryptedLen > 0, the buffer contains some encrypted data that
+	 * should be written out to the client.
+	 *
+	 * When encryptedLen == 0, call encryptBuffer() to load the buffer with
+	 * more data, and encrypt it.
+	 */
+	char	   *encryptBuffer;
+	int			encryptBufferSize;
+	int			encryptBufferLen;
+
+	char	   *encryptedData;
+	size_t		encryptedLen;
+
+	/*
+	 * Number of plaintext bytes the buffer currently holds in encrypted form.
+	 * This is less than the number of encrypted bytes, because the SSL
+	 * protocol adds framing. (it could also be more, when compression is used)
+	 */
+	int			plaintextLen;
+
+	/*
+	 * Buffer for decrypting read data.
+	 *
+	 * decryptBufferSize is the allocated size of decryptBuffer.
+	 * decryptBufferLen is the number of ciphertext bytes loaded to the buffer.
+	 *
+	 * The buffer can be in one of two states. In the first state, it contains
+	 * zero or more raw bytes, and more can be loaded into it. In the other
+	 * state, it contains decrypted data that can be read out.
+	 *
+	 * When the buffer contains decrypted data, decryptedLen > 0, and
+	 * decryptedData points to the data. The buffer can also contain some
+	 * leftover raw data that has not been decrypted yet (extraData/extraLen).
+	 *
+	 * Before reading more raw data to the buffer, call
+	 * prepareDecryptBufferForRead. It moves any leftover raw data to the
+	 * beginning of the buffer.
+	 */
+	char	   *decryptBuffer;
+	int			decryptBufferSize;
+	int			decryptBufferLen; 	/* ciphertext bytes loaded in decryptBuffer */
+
+	/* decrypted data not returned to the caller yet */
+	char	   *decryptedData;
+	int			decryptedLen;
+
+	char	   *extraData;
+	int			extraLen;
+};
+
+typedef struct SchannelContext SchannelContext;
+
+/*
+ * Somewhat arbitrarily, use a 10k buffer for both input and output.
+ *
+ * XXX: A buffer size somewhat larger than 8k probably makes sense at least
+ * for input, because the pqcomm.c uses an 8k buffer. (the extra is to contain
+ * SSL headers in the raw input). But I haven't done any performance testing.
+ */
+#define BUFSIZE 10000
+
+/* Functions for managing the encrypt/decrypt buffers */
+static ssize_t encryptBuffer(SchannelContext *ctx, void *ptr, int len);
+static void prepareDecryptBufferForRead(SchannelContext *ctx);
+static int decryptBuffer(SchannelContext *ctx);
+
+static int schannel_handshake(Port *port);
+
+/* Functions for dealing with certificates */
+static void importServerKey(void);
+static CERT_CONTEXT *getServerCert(void);
+static CERT_CONTEXT *getClientRootCert(void);
+static bool validateClientCert(CERT_CONTEXT *cert);
+
+/* Functions for converting error codes to strings */
+static const char *getSecurityStatus(SECURITY_STATUS dwerror);
+
+static char *slurpFile(const char *path, int *size);
+
+/*
+ *	Write data to a secure connection.
+ */
+ssize_t
+be_tls_write(Port *port, void *ptr, size_t len)
+{
+	SchannelContext *ctx = port->windows_schannel;
+	int			sendlen;
+
+	/*
+	 * If the output buffer is empty, encrypt (some of) the input data to fill
+	 * it.
+	 */
+	if (ctx->encryptedLen == 0)
+	{
+		int			plaintextLen;
+
+		plaintextLen = encryptBuffer(ctx, ptr, len);
+		if (plaintextLen < 0)
+			return -1;
+		ctx->plaintextLen = plaintextLen;
+	}
+
+	/*
+	 * Write out the encrypted data in the buffer
+	 */
+	sendlen = secure_raw_write(port,
+							   ctx->encryptedData,
+							   ctx->encryptedLen);
+	if (sendlen <= 0)
+		return sendlen;
+
+	ctx->encryptedData += sendlen;
+	ctx->encryptedLen -= sendlen;
+
+	if (ctx->encryptedLen == 0)
+	{
+		/* sent all the data in the buffer. */
+		return ctx->plaintextLen;
+	}
+	else
+	{
+		/* return 0 to indicate that the caller has to retry. */
+		return 0;
+	}
+}
+
+/*
+ * Encrypt more data. Helper function for be_tls_write().
+ */
+static ssize_t
+encryptBuffer(SchannelContext *ctx, void *ptr, int len)
+{
+	SecPkgContext_StreamSizes sizes;
+	SecBufferDesc sbufdesc;
+	SecBuffer	sbufs[4];
+	SECURITY_STATUS rc;
+
+	/* we should've sent out all encrypted data from previous round first */
+	Assert(ctx->encryptedLen == 0);
+
+	rc = QueryContextAttributes(&ctx->ctxHandle,
+								SECPKG_ATTR_STREAM_SIZES, &sizes);
+	if (rc != SEC_E_OK)
+	{
+		elog(COMMERROR, "QueryContextAttributes failed: %s", getSecurityStatus(rc));
+		return -1;
+	}
+	if (sizes.cbHeader + sizes.cbTrailer + len > ctx->encryptBufferSize)
+		len = ctx->encryptBufferSize - (sizes.cbHeader + sizes.cbTrailer);
+
+	memcpy(&ctx->encryptBuffer[sizes.cbHeader], ptr, len);
+
+	sbufs[0].pvBuffer = ctx->encryptBuffer;
+	sbufs[0].cbBuffer = sizes.cbHeader;
+	sbufs[0].BufferType = SECBUFFER_STREAM_HEADER;
+
+	sbufs[1].pvBuffer = &ctx->encryptBuffer[sizes.cbHeader];
+	sbufs[1].cbBuffer = len;
+	sbufs[1].BufferType = SECBUFFER_DATA;
+
+	sbufs[2].pvBuffer = &ctx->encryptBuffer[sizes.cbHeader + len];
+	sbufs[2].cbBuffer = sizes.cbTrailer;
+	sbufs[2].BufferType = SECBUFFER_STREAM_TRAILER;
+
+	sbufs[3].pvBuffer = NULL;
+	sbufs[3].cbBuffer = 0;
+	sbufs[3].BufferType = SECBUFFER_EMPTY;
+
+	sbufdesc.ulVersion = SECBUFFER_VERSION;
+	sbufdesc.cBuffers = 4;
+	sbufdesc.pBuffers = sbufs;
+
+	rc = EncryptMessage(&ctx->ctxHandle, 0, &sbufdesc, 0);
+	if (rc != SEC_E_OK)
+	{
+		elog(ERROR, "EncryptMessage failed: %s", getSecurityStatus(rc));
+		return -1;
+	}
+
+	ctx->encryptedData = ctx->encryptBuffer;
+	ctx->encryptedLen = sbufs[0].cbBuffer + sbufs[1].cbBuffer + sbufs[2].cbBuffer;
+
+	return len;
+}
+
+/*
+ *	Initialize global SSL context.
+ */
+void
+be_tls_init(void)
+{
+	/*
+	 * Import the server's private key into the key container. (This only
+	 * needs to be done once at server startup.)
+	 */
+	if (IsUnderPostmaster)
+		importServerKey();
+}
+
+static int
+schannel_handshake(Port *port)
+{
+	SchannelContext *ctx = port->windows_schannel;
+	SecBufferDesc insbufdesc;
+	SecBuffer	insbufs[2];
+	SecBufferDesc outsbufdesc;
+	SecBuffer	outsbufs[1];
+	DWORD		flags;
+	DWORD		outflags;
+	SECURITY_STATUS rc;
+	ssize_t		readlen;
+	bool		handshake_complete = false;
+
+	do
+	{
+		/* read more raw data to buffer */
+		readlen = secure_raw_read(port,
+								  &ctx->decryptBuffer[ctx->decryptBufferLen],
+								  ctx->decryptBufferSize - ctx->decryptBufferLen);
+		if (readlen < 0)
+		{
+			ereport(COMMERROR,
+					(errcode_for_socket_access(),
+					 errmsg("could not accept SSL connection: %m")));
+			be_tls_close(port);
+			return -1;
+		}
+
+		ctx->decryptBufferLen += readlen;
+
+		/*
+		 * Pass the raw data read this far to AcceptSecurityContext.
+		 * Per Microsoft documentation, AcceptSecurityContext requires two
+		 * input buffers, one with SECBUFFER_TOKEN containing the input data,
+		 * and one SECBUFFER_EMPTY.
+		 */
+		insbufs[0].pvBuffer = ctx->decryptBuffer;
+		insbufs[0].cbBuffer = ctx->decryptBufferLen;
+		insbufs[0].BufferType = SECBUFFER_TOKEN;
+
+		insbufs[1].pvBuffer = NULL;
+		insbufs[1].cbBuffer = 0;
+		insbufs[1].BufferType = SECBUFFER_EMPTY;
+
+		insbufdesc.cBuffers = 2;
+		insbufdesc.pBuffers = insbufs;
+		insbufdesc.ulVersion = SECBUFFER_VERSION;
+
+		/*
+		 * The ASC_REQ_MUTUAL_AUTH means that we request a client cert. The
+		 * connection will still succeed if the client doesn't provide one.
+		 */
+		flags = ASC_REQ_ALLOCATE_MEMORY |
+			ASC_REQ_CONFIDENTIALITY |
+			ASC_RET_EXTENDED_ERROR |
+			ASC_REQ_REPLAY_DETECT |
+			ASC_REQ_SEQUENCE_DETECT |
+			ASC_REQ_MUTUAL_AUTH |
+			ASC_REQ_STREAM;
+
+		outsbufs[0].pvBuffer   = NULL;
+		outsbufs[0].BufferType = SECBUFFER_TOKEN;
+		outsbufs[0].cbBuffer   = 0;
+
+		outsbufdesc.cBuffers = 1;
+		outsbufdesc.pBuffers = outsbufs;
+		outsbufdesc.ulVersion = SECBUFFER_VERSION;
+
+		rc = AcceptSecurityContext(&ctx->credHandle,
+								   ctx->ctxHandle_valid ? &ctx->ctxHandle : NULL,
+								   &insbufdesc,
+								   flags,
+								   0,
+								   ctx->ctxHandle_valid ? NULL : &ctx->ctxHandle,
+								   &outsbufdesc,
+								   &outflags,
+								   NULL);
+		ctx->ctxHandle_valid = true;
+
+		/*
+		 * If AcceptSecurityContext returns SEC_E_OK or SEC_I_CONTINUE_NEEDED,
+		 * it has consumed (some of) the raw input. It may have also produced
+		 * some output that we need to write back to the client.
+		 */
+		if (rc == SEC_E_OK || rc == SEC_I_CONTINUE_NEEDED)
+		{
+			/*
+			 * AcceptSecurityContext might not have processed all the input
+			 * data. The number of unprocessed bytes is returned in input
+			 * buffer 1, as an SECBUFFER_EXTRA buffer.
+			 */
+			if (insbufs[1].BufferType == SECBUFFER_EXTRA)
+			{
+				size_t extraLen = insbufs[1].cbBuffer;
+
+				memmove(ctx->decryptBuffer, &ctx->decryptBuffer[ctx->decryptBufferLen - extraLen], extraLen);
+				ctx->decryptBufferLen = extraLen;
+			}
+			else
+				ctx->decryptBufferLen = 0;
+
+			if (outsbufs[0].pvBuffer)
+			{
+				ssize_t		totalsent = 0;
+				ssize_t		sendlen;
+
+				while(totalsent < outsbufs[0].cbBuffer)
+				{
+					sendlen = secure_raw_write(port,
+											   outsbufs[0].pvBuffer,
+											   outsbufs[0].cbBuffer);
+					/* Note: we assume that the socket is in blocking mode */
+					if (sendlen <= 0)
+					{
+						ereport(COMMERROR,
+								(errcode(ERRCODE_PROTOCOL_VIOLATION),
+								 errmsg("could not send")));
+						return -1;
+					}
+
+					totalsent += sendlen;
+				}
+
+				FreeContextBuffer(outsbufs[0].pvBuffer);
+				outsbufs[0].pvBuffer = NULL;
+			}
+		}
+
+		switch(rc)
+		{
+			case SEC_E_OK:
+				/* SSL handshake is complete */
+				handshake_complete = true;
+				break;
+
+			case SEC_I_COMPLETE_AND_CONTINUE:
+				elog(COMMERROR, "SEC_I_COMPLETE_AND_CONTINUE not implemented", rc);
+				be_tls_close(port);
+				return -1;
+
+			case SEC_I_COMPLETE_NEEDED:
+				elog(COMMERROR, "SEC_I_COMPLETE_NEEDED not implemented", rc);
+				be_tls_close(port);
+				return -1;
+
+			case SEC_I_CONTINUE_NEEDED:
+				/* Need more input from the client to continue */
+				break;
+
+			case SEC_I_INCOMPLETE_CREDENTIALS:
+				elog(COMMERROR, "SEC_I_INCOMPLETE_CREDENTIALS not implemented");
+				be_tls_close(port);
+				return -1;
+
+			case SEC_E_INCOMPLETE_MESSAGE:
+				/*
+				 * The input didn't contain a full SSL message. Loop back to
+				 * read more data, leaving the input already read in the
+				 * buffer, and retry.
+				 */
+				break;
+
+			default:
+				ereport(COMMERROR,
+						(errcode(ERRCODE_PROTOCOL_VIOLATION),
+						 errmsg("could not initialize SSL connection"),
+						 errdetail("AcceptSecurityContext failed: %s", getSecurityStatus(rc))));
+				be_tls_close(port);
+				return -1;
+		}
+	} while (!handshake_complete);
+
+	/* success! */
+	return 0;
+}
+
+/*
+ *	Attempt to negotiate SSL connection.
+ */
+int
+be_tls_open_server(Port *port)
+{
+	SchannelContext *ctx = port->windows_schannel;
+	SECURITY_STATUS rc;
+	SCHANNEL_CRED credData;
+	CERT_CONTEXT *serverCert;
+	CERT_CONTEXT *remoteCert;
+
+	serverCert = getServerCert();
+
+	memset(&credData, 0, sizeof(credData));
+	credData.dwVersion = SCHANNEL_CRED_VERSION;
+	credData.dwFlags |= SCH_CRED_NO_SYSTEM_MAPPER;
+	credData.cCreds = 1;
+	credData.paCred = &serverCert;
+
+	ctx = (SchannelContext *) palloc0(sizeof(SchannelContext));
+
+	rc = AcquireCredentialsHandle(NULL,
+								  UNISP_NAME,
+								  SECPKG_CRED_INBOUND,
+								  NULL,
+								  &credData,
+								  NULL,
+								  NULL,
+								  &ctx->credHandle,
+								  NULL);
+	if (rc != SEC_E_OK)
+	{
+		ereport(COMMERROR,
+				(errcode(ERRCODE_PROTOCOL_VIOLATION),
+				 errmsg("could not initialize SSL"),
+				 errdetail("AcquireCredentialsHandle failed: %s", getSecurityStatus(rc))));
+		be_tls_close(port);
+		return -1;
+	}
+	ctx->credHandle_valid = true;
+
+	ctx->encryptBufferSize = BUFSIZE;
+	ctx->encryptBuffer = palloc(ctx->encryptBufferSize);
+	ctx->decryptBufferSize = BUFSIZE;
+	ctx->decryptBuffer = palloc(ctx->encryptBufferSize);
+
+	port->windows_schannel = ctx;
+	port->ssl_in_use = true;
+
+	if (schannel_handshake(port) < 0)
+		return -1;
+
+	/* SSL handshake complete */
+	elog(SCHANNEL_DEBUG, "SSL handshake completed");
+
+	/* Get client certificate, if available */
+	rc = QueryContextAttributes(&ctx->ctxHandle,
+								SECPKG_ATTR_REMOTE_CERT_CONTEXT,
+								(PVOID) &remoteCert);
+	if (rc == SEC_E_OK)
+	{
+		/*
+		 * Validate the client cert. If it passes, extract the Common Name,
+		 * to be used later in cert authentication.
+		 */
+		if (validateClientCert(remoteCert))
+		{
+			char	   *cn;
+			DWORD		len;
+			DWORD		len2;
+
+			port->peer_cert_valid = true;
+			elog(SCHANNEL_DEBUG, "client certificate validated");
+
+			/*
+			 * Call CertGetNameString without passing a buffer, to get the
+			 * required length.
+			 */
+			len = CertGetNameString(remoteCert,
+									CERT_NAME_ATTR_TYPE,
+									0,
+									szOID_COMMON_NAME,
+									NULL,
+									0);
+			if (len > 1)
+			{
+				cn = palloc(len);
+				len2 = CertGetNameString(remoteCert,
+										 CERT_NAME_ATTR_TYPE,
+										 0,
+										 szOID_COMMON_NAME,
+										 cn,
+										 len);
+				if (len != len2)
+				{
+					/* shouldn't happen */
+					pfree(cn);
+					be_tls_close(port);
+					return -1;
+				}
+				port->peer_cn = cn;
+				elog(SCHANNEL_DEBUG, "client cert name: %s", cn);
+			}
+		}
+		else
+			elog(SCHANNEL_DEBUG, "client certificate validation failed");
+
+		CertFreeCertificateContext(remoteCert);
+	}
+
+	return 0;
+}
+
+/*
+ *	Close SSL connection.
+ */
+void
+be_tls_close(Port *port)
+{
+	SchannelContext *ctx = port->windows_schannel;
+	DWORD		token;
+	SecBufferDesc outsbufdesc;
+	SecBuffer	outsbufs[1];
+	SECURITY_STATUS rc;
+
+	if (ctx->ctxHandle_valid)
+	{
+		/*
+		 *  "Shutting Down an Schannel Connection",
+		 *  http://msdn.microsoft.com/en-us/library/windows/desktop/aa380138%28v=vs.85%29.aspx
+		 */
+		token = SCHANNEL_SHUTDOWN;
+
+		outsbufdesc.ulVersion = SECBUFFER_VERSION;
+		outsbufdesc.cBuffers = 1;
+		outsbufdesc.pBuffers = outsbufs;
+
+		outsbufs[0].BufferType = SECBUFFER_TOKEN;
+		outsbufs[0].cbBuffer = sizeof( token );
+		outsbufs[0].pvBuffer = &token;
+
+		rc = ApplyControlToken(&ctx->ctxHandle, &outsbufdesc);
+
+		/*
+		 * Use AcceptSecurityContext to finish the shutdown, just like the
+		 * initiation handshake is done.
+		 */
+		(void) schannel_handshake(port);
+
+		DeleteSecurityContext(&ctx->ctxHandle);
+		ctx->ctxHandle_valid = false;
+	}
+	if (ctx->credHandle_valid)
+	{
+		FreeCredentialsHandle(&ctx->credHandle);
+		ctx->credHandle_valid = false;
+	}
+
+	if (ctx->encryptBuffer)
+		pfree(ctx->encryptBuffer);
+	if (ctx->decryptBuffer)
+		pfree(ctx->decryptBuffer);
+
+	pfree(ctx);
+
+	if (port->peer_cn)
+	{
+		pfree(port->peer_cn);
+		port->peer_cn = NULL;
+	}
+	port->peer_cert_valid = false;
+
+	port->windows_schannel = NULL;
+	port->ssl_in_use = false;
+}
+
+/*
+ *	Read data from a secure connection.
+ */
+ssize_t
+be_tls_read(Port *port, void *ptr, size_t len)
+{
+	SchannelContext *ctx = port->windows_schannel;
+	int			readlen;
+	int			maxsize;
+	size_t		totalreadlen = 0;
+	char	   *dst = ptr;
+
+	for (;;)
+	{
+		/* if we already have some decrypted data, copy it to the target buffer */
+		if (ctx->decryptedLen > 0)
+		{
+			maxsize = len - totalreadlen;
+			if (maxsize > ctx->decryptedLen)
+				maxsize = ctx->decryptedLen;
+			memcpy(dst, &ctx->decryptedData[totalreadlen], maxsize);
+			dst += maxsize;
+			totalreadlen += maxsize;
+			ctx->decryptedLen -= maxsize;
+			ctx->decryptedData += maxsize;
+		}
+
+		/* If we have some data to return, return it. */
+		if (totalreadlen > 0)
+		{
+			return totalreadlen;
+		}
+
+		/*
+		 * there shouldn't be any decrypted data left in the buffer, we copied
+		 * it all to the target.
+		 */
+		Assert(ctx->decryptedLen == 0);
+
+		/*
+		 * Need to decrypt more data, and for that, need to read more raw
+		 * data.
+		 */
+		prepareDecryptBufferForRead(ctx);
+		while (ctx->decryptedLen == 0)
+		{
+			/* read more data to buffer */
+			maxsize = ctx->decryptBufferSize - ctx->decryptBufferLen;
+			readlen = secure_raw_read(port,
+									  &ctx->decryptBuffer[ctx->decryptBufferLen],
+									  maxsize);
+			if (readlen < 0)
+				return readlen;
+			if (readlen == 0)
+			{
+				/* this implies we're in non-blocking mode */
+				return totalreadlen;
+			}
+
+			ctx->decryptBufferLen += readlen;
+
+			/* decrypt what we got */
+			if (decryptBuffer(ctx) == -1)
+				return -1;
+			if (ctx->remote_shutdown)
+				return totalreadlen;
+
+			/* If decryptBuffer needs more data, decrypteLen is still 0. */
+		}
+	}
+
+	return totalreadlen;
+}
+
+/*
+ * Prepare the buffer for reading in more cipher text.
+ */
+static void
+prepareDecryptBufferForRead(SchannelContext *ctx)
+{
+	/*
+	 * should've consumed all the previous decrypted data in the buffer befor
+	 * decrypting more.
+	 */
+	Assert(ctx->decryptedLen == 0);
+
+	/*
+	 * If there's any ciphertext left in the buffer from previous round, move
+	 * it to beginning.
+	 */
+	if (ctx->extraLen > 0)
+		memmove(ctx->decryptBuffer,	ctx->extraData,	ctx->extraLen);
+	ctx->decryptBufferLen = ctx->extraLen;
+	ctx->extraData = NULL;
+	ctx->extraLen = 0;
+	ctx->decryptedData = NULL;
+	ctx->decryptedLen = 0;
+}
+
+/*
+ * Try to decrypt the data currently in decryptBuffer. On success,
+ * decryptedData points to the decrypted data. DecryptMessage decrypts
+ * in-place, so decryptedData points to somewhere within the buffer!
+ *
+ * On success, returns 1. On error, returns -1. If more raw data is needed
+ * to decrypt, returns 0. If the client shut down the connection, also
+ * returns 0, and sets ctx->remote_shutdown flag.
+ */
+static int
+decryptBuffer(SchannelContext *ctx)
+{
+	/*
+	 * Call DecryptBuffer until it returns some data, fails, or indicates that
+	 * it needs more input
+	 */
+	while(ctx->decryptedLen == 0)
+	{
+		SecBufferDesc sbufdesc;
+		SecBuffer	sbufs[4];
+		SECURITY_STATUS rc;
+		int			i;
+
+		Assert(ctx->decryptBufferLen > 0);
+		Assert(ctx->extraLen == 0);
+
+		sbufs[0].pvBuffer = ctx->decryptBuffer;
+		sbufs[0].cbBuffer = ctx->decryptBufferLen;
+		sbufs[0].BufferType = SECBUFFER_DATA;
+		sbufs[1].pvBuffer = NULL;
+		sbufs[1].cbBuffer = 0;
+		sbufs[1].BufferType   = SECBUFFER_EMPTY;
+		sbufs[2].pvBuffer = NULL;
+		sbufs[2].cbBuffer = 0;
+		sbufs[2].BufferType   = SECBUFFER_EMPTY;
+		sbufs[3].pvBuffer = NULL;
+		sbufs[3].cbBuffer = 0;
+		sbufs[3].BufferType   = SECBUFFER_EMPTY;
+
+		sbufdesc.ulVersion = SECBUFFER_VERSION;
+		sbufdesc.cBuffers = 4;
+		sbufdesc.pBuffers = sbufs;
+
+		rc = DecryptMessage(&ctx->ctxHandle, &sbufdesc, 0, NULL);
+
+		if (rc == SEC_E_INCOMPLETE_MESSAGE)
+		{
+			/* We don't have the full message yet. Need more data. */
+			return 0;
+		}
+		if (rc == SEC_I_CONTEXT_EXPIRED)
+		{
+			elog(SCHANNEL_DEBUG, "DecryptMessage returned SEC_I_CONTEXT_EXPIRED");
+			ctx->remote_shutdown = true;
+			return 0;
+		}
+		if (rc == SEC_I_RENEGOTIATE)
+		{
+			/* TODO */
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("SSL renegotiated not implemented")));
+		}
+		if (rc != SEC_E_OK)
+		{
+			/* FIXME: set errno */
+			elog(COMMERROR, "DecryptMessage failed: %s", getSecurityStatus(rc));
+			return -1;
+		}
+
+		for (i = 0; i < 4; i++)
+		{
+			if (sbufs[i].BufferType == SECBUFFER_EXTRA)
+			{
+				ctx->extraData = sbufs[i].pvBuffer;
+				ctx->extraLen = sbufs[i].cbBuffer;
+			}
+			if (sbufs[i].BufferType == SECBUFFER_DATA)
+			{
+				ctx->decryptedData = sbufs[i].pvBuffer;
+				ctx->decryptedLen = sbufs[i].cbBuffer;
+			}
+		}
+		if (ctx->decryptedLen == 0)
+		{
+			/*
+			 * DecryptMessage didn't decrypt anything. Shift the extra data
+			 * to beginning of buffer, and retry.
+			 */
+			prepareDecryptBufferForRead(ctx);
+		}
+	}
+
+	return 1;
+}
+
+/**** Certificate handling functions ****/
+
+static char *
+slurpFile(const char *path, int *size)
+{
+	struct stat statbuf;
+	FILE	   *fp;
+	char	   *content;
+	int			r;
+
+	if (stat(path, &statbuf) != 0)
+	{
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not stat certificate file \"%s\": %m",
+						path)));
+	}
+	fp = AllocateFile(path, "r");
+	if (!fp)
+	{
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not read certificate file \"%s\": %m",
+						path)));
+	}
+	content = palloc(statbuf.st_size + 1);
+	r = fread(content, statbuf.st_size, 1, fp);
+	content[statbuf.st_size] = '\0';
+
+	if (r != 1 || ferror(fp) || FreeFile(fp))
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not read file \"%s\": %m",
+						path)));
+
+	if (size)
+		*size = statbuf.st_size;
+
+	return content;
+}
+
+/*
+ * Import the server's private key into the key container.
+ */
+static void
+importServerKey(void)
+{
+	char	   *content;
+	int			filelen;
+	char		buf[50000];
+	DWORD		buflen = 50000;
+	char		privKeyBlob[50000];
+	DWORD		privKeyBlobLen = 50000;
+	HCRYPTPROV	cryptProvHandle;
+	HCRYPTKEY	keyHandle;
+
+	content = slurpFile(ssl_key_file, &filelen);
+
+	/* convert from PEM to DER */
+	buflen = 50000;
+	if (!CryptStringToBinary(content, filelen, CRYPT_STRING_BASE64HEADER,
+							 buf, &buflen, NULL, NULL))
+	{
+		elog(ERROR, "CryptStringToBinary (key) failed: 0x%x", GetLastError());		
+	}
+	pfree(content);
+
+	if (!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+							 PKCS_RSA_PRIVATE_KEY,
+							 buf, buflen,
+							 0,
+							 NULL,
+							 privKeyBlob,
+							 &privKeyBlobLen))
+	{
+		elog(ERROR, "CryptDecodeObjectEx (key) failed: 0x%x", GetLastError());
+	}
+
+	/* Open the key container */
+	if (!CryptAcquireContextW(&cryptProvHandle, PG_KEY_CONTAINER_NAME, MS_DEF_RSA_SCHANNEL_PROV_W, PROV_RSA_SCHANNEL, 0))
+	{
+		if (GetLastError() == NTE_BAD_KEYSET)
+		{
+			/*
+			 * The key container could not be accessed. Most probable cause
+			 * is that it doesn't exist, so try to create it.
+			 */
+			if (!CryptAcquireContextW(&cryptProvHandle, PG_KEY_CONTAINER_NAME, MS_DEF_RSA_SCHANNEL_PROV_W, PROV_RSA_SCHANNEL, CRYPT_NEWKEYSET))
+			{
+				elog(ERROR, "CryptAcquireContext failed: 0x%x", GetLastError());
+			}
+			else
+				elog(LOG, "SChannel key container created");
+		}
+		else
+			elog(ERROR, "CryptAcquireContext failed: 0x%x", GetLastError());
+	}
+
+	if (!CryptImportKey(cryptProvHandle, privKeyBlob, privKeyBlobLen,
+						(HCRYPTKEY) NULL, 0, &keyHandle))
+	{
+		elog(ERROR, "CryptImportKey failed: 0x%x", GetLastError());
+	}
+
+	CryptDestroyKey(keyHandle);
+
+	if (!CryptReleaseContext(cryptProvHandle, 0))
+		elog(ERROR, "CryptReleaseContext failed: 0x%x", GetLastError());
+}
+
+static CERT_CONTEXT *
+getServerCert(void)
+{
+	CERT_CONTEXT *cert_cxt;
+	char	   *content;
+	int			filelen;
+	char		buf[50000];
+	DWORD		buflen = 50000;
+	CRYPT_KEY_PROV_INFO keyProvInfo;
+
+	content = slurpFile(ssl_cert_file, &filelen);
+
+	/* convert from PEM to DER */
+	if (!CryptStringToBinary(content, filelen, CRYPT_STRING_BASE64HEADER,
+							 buf, &buflen, NULL, NULL))
+	{
+		elog(ERROR, "CryptStringToBinary failed: 0x%x", GetLastError());		
+	}
+
+	cert_cxt = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+											buf,
+											buflen);
+	if (cert_cxt == NULL)
+	{
+		elog(ERROR, "CertCreateCertificateContext failed: 0x%x", GetLastError());
+	}
+	pfree(content);
+
+	/* Tell the Crypto API how to find the private key */
+	memset(&keyProvInfo, 0, sizeof(keyProvInfo));
+	keyProvInfo.pwszContainerName = PG_KEY_CONTAINER_NAME;
+	keyProvInfo.pwszProvName = MS_DEF_RSA_SCHANNEL_PROV_W;
+	keyProvInfo.dwProvType = PROV_RSA_SCHANNEL;
+	keyProvInfo.dwKeySpec = AT_KEYEXCHANGE;
+
+	if (!CertSetCertificateContextProperty(cert_cxt,
+										   CERT_KEY_PROV_INFO_PROP_ID,
+										   0,
+										   &keyProvInfo))
+	{
+		elog(ERROR, "CertSetCertificateContextProperty failed: %x", GetLastError());
+	}
+
+	return cert_cxt;
+}
+
+static bool
+validateClientCert(CERT_CONTEXT *cert)
+{
+	CERT_CONTEXT *rootCert;
+	DWORD flags;
+
+	/*
+	 * Per MSDN documentation [1], we must check the following things:
+	 *
+	 * 1. The certificate chain is complete and the root is a certificate from
+	 *    a trusted certification authority (CA).
+	 * 2. The current time is not beyond the begin and end dates for each of
+	 *    the certificates in the certificate chain.
+	 * 3. None of the certificates in the certificate chain have been revoked.
+	 * 4. The depth of the leaf certificate is not deeper than the maximum
+	 *    allowable depth specified in the certificate extension. This check is
+	 *    only necessary if there is a depth specified.
+	 * 5. The usage of the certificate is correct, for example, a client
+	 *    certificate should not be used to authenticate a server.
+	 *
+	 * [1] "Manually Validating Schannel Credential",
+	 *     http://msdn.microsoft.com/en-us/library/windows/desktop/aa378740%28v=vs.85%29.aspx
+	 */
+
+	/*
+	 * 1. Check the chain
+	 * 2. Check expiration
+	 *
+	 * TODO: Currently, we only support client certs signed by the root client
+	 * CA. Intermediary CA's are not supported.
+	 */
+	rootCert = getClientRootCert();
+
+	/* TODO: CRL's are not actually supported */
+	flags = CERT_STORE_SIGNATURE_FLAG |
+		CERT_STORE_TIME_VALIDITY_FLAG;
+	if (!CertVerifySubjectCertificateContext(
+			cert,
+			rootCert,
+			&flags))
+	{
+		elog(LOG, "failed to verify client cert");
+		return false;
+	}
+	if (flags != 0)
+	{
+		if ((flags & CERT_STORE_SIGNATURE_FLAG) != 0)
+			elog(LOG, "signature check failed on client cert");
+		if ((flags & CERT_STORE_REVOCATION_FLAG) != 0)
+			elog(LOG, "client cert was revoked");
+		if ((flags & CERT_STORE_TIME_VALIDITY_FLAG) != 0)
+			elog(LOG, "client cert is not valid at this time");
+
+		return false;
+	}
+
+	/*
+	 * We succeeded to get the issuer's certificate from the trusted root store,
+	 * and the signature on the client cert passed, so we're good to go.
+	 */
+
+	return true;
+}
+
+static CERT_CONTEXT *
+getClientRootCert(void)
+{
+	char	  *client_ca_file;
+	int			client_ca_file_len;
+	char		crtBuf[50000]; /* TODO: enlarge as needed */
+	DWORD		crtBufLen = 50000;
+	CERT_CONTEXT *client_root_cxt;
+
+	client_ca_file = slurpFile(ssl_ca_file, &client_ca_file_len);
+	if (client_ca_file == NULL)
+		return NULL;
+
+	/* convert from PEM to DER */
+	if (!CryptStringToBinary(
+			client_ca_file,
+			client_ca_file_len,
+			CRYPT_STRING_BASE64HEADER,
+			crtBuf,
+			&crtBufLen,
+			NULL,
+			NULL))
+	{
+		elog(ERROR, "failed to convert client root cert from PEM to DER format");
+	}
+
+	client_root_cxt = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+												   crtBuf,
+												   crtBufLen);
+	if (client_root_cxt == NULL)
+		elog(ERROR, "CertCreateCertificateContext failed: 0x%x", GetLastError());
+
+	return client_root_cxt;
+}
+
+/*** Printing error codes ***/
+
+#define ERRORCODE_STR(x) { x, #x }
+
+typedef struct
+{
+	uint32 rc;
+	const char *str;
+} errorcode_str;
+
+static const char *
+getErrorStr(uint32 rc, errorcode_str *map)
+{
+	static char buf[100];
+	int i;
+
+	for (i = 0; map[i].str != NULL; i++)
+	{
+		if (map[i].rc == rc)
+			return map[i].str;
+	}
+	snprintf(buf, sizeof(buf), "0x%X", rc);
+	return buf;
+}
+
+static const char *
+getSecurityStatus(SECURITY_STATUS rc)
+{
+	static errorcode_str sec_status_codes[] = {
+		ERRORCODE_STR(SEC_E_OK),
+		ERRORCODE_STR(SEC_E_INCOMPLETE_MESSAGE),
+		ERRORCODE_STR(SEC_E_INSUFFICIENT_MEMORY),
+		ERRORCODE_STR(SEC_E_INTERNAL_ERROR),
+		ERRORCODE_STR(SEC_E_INVALID_HANDLE),
+		ERRORCODE_STR(SEC_E_INVALID_TOKEN),
+		ERRORCODE_STR(SEC_E_LOGON_DENIED),
+		ERRORCODE_STR(SEC_E_NO_AUTHENTICATING_AUTHORITY),
+		ERRORCODE_STR(SEC_E_NO_CREDENTIALS),
+		ERRORCODE_STR(SEC_I_COMPLETE_AND_CONTINUE),
+		ERRORCODE_STR(SEC_I_COMPLETE_NEEDED),
+		ERRORCODE_STR(SEC_I_CONTINUE_NEEDED),
+		ERRORCODE_STR(SEC_E_ILLEGAL_MESSAGE),
+		{ 0, NULL }
+	};
+	return getErrorStr(rc, sec_status_codes);
+}
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index 41ec1ad..fcd5ea2 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -103,6 +103,10 @@ secure_open_server(Port *port)
 	r = be_tls_open_server(port);
 #endif
 
+	ereport(DEBUG2,
+			(errmsg("SSL connection from \"%s\"",
+					port->peer_cn ? port->peer_cn : "(anonymous)")));
+
 	return r;
 }
 
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 34e52e4..7fcee98 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -53,6 +53,10 @@
 #include <security.h>
 #undef SECURITY_WIN32
 
+#ifdef USE_WINDOWS_SCHANNEL
+struct SchannelContext;
+#endif
+
 #ifndef ENABLE_GSS
 /*
  * Define a fake structure compatible with GSSAPI on Unix.
@@ -197,6 +201,9 @@ typedef struct Port
 	X509	   *peer;
 	unsigned long count;
 #endif
+#ifdef USE_WINDOWS_SCHANNEL
+	struct SchannelContext *windows_schannel;
+#endif
 } Port;
 
 #ifdef USE_SSL
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 5bdfa47..e4d8574 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -802,6 +802,9 @@
 /* Define to select Win32-style shared memory. */
 #undef USE_WIN32_SHARED_MEMORY
 
+/* Define to build with Windows SChannel support. (--with-winschannel) */
+#undef USE_WINDOWS_SCHANNEL
+
 /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
    significant byte first (like Motorola and SPARC, unlike Intel). */
 #if defined AC_APPLE_UNIVERSAL_BUILD
diff --git a/src/include/pg_config.h.win32 b/src/include/pg_config.h.win32
index 00be15f..901de03 100644
--- a/src/include/pg_config.h.win32
+++ b/src/include/pg_config.h.win32
@@ -649,6 +649,9 @@
 /* Define to select Win32-style semaphores. */
 #define USE_WIN32_SEMAPHORES 1
 
+/* Define to build with Windows SChannel support. (--with-winschannel) */
+#undef USE_WINDOWS_SCHANNEL
+
 /* Number of bits in a file offset, on hosts where this is settable. */
 /* #undef _FILE_OFFSET_BITS */
 
diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h
index d78f38e..54ed4b2 100644
--- a/src/include/pg_config_manual.h
+++ b/src/include/pg_config_manual.h
@@ -146,10 +146,9 @@
 
 /*
  * 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_WINDOWS_SCHANNEL)
 #define USE_SSL
 #endif
 
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index a90cb89..7968266 100644
--- a/src/interfaces/libpq/Makefile
+++ b/src/interfaces/libpq/Makefile
@@ -48,6 +48,10 @@ ifeq ($(with_openssl),yes)
 OBJS += fe-secure-openssl.o
 endif
 
+ifeq ($(with_winschannel),yes)
+OBJS += fe-secure-winschannel.o
+endif
+
 ifeq ($(PORTNAME), cygwin)
 override shlib = cyg$(NAME)$(DLSUFFIX)
 endif
diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
index f950fc3..cee7b2e 100644
--- a/src/interfaces/libpq/fe-secure-openssl.c
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -780,57 +780,21 @@ destroy_ssl_system(void)
 static int
 initialize_SSL(PGconn *conn)
 {
-	struct stat buf;
-	char		homedir[MAXPGPATH];
-	char		fnbuf[MAXPGPATH];
-	char		sebuf[256];
-	bool		have_homedir;
-	bool		have_cert;
 	EVP_PKEY   *pkey = NULL;
-
-	/*
-	 * We'll need the home directory if any of the relevant parameters are
-	 * defaulted.  If pqGetHomeDirectory fails, act as though none of the
-	 * files could be found.
-	 */
-	if (!(conn->sslcert && strlen(conn->sslcert) > 0) ||
-		!(conn->sslkey && strlen(conn->sslkey) > 0) ||
-		!(conn->sslrootcert && strlen(conn->sslrootcert) > 0) ||
-		!(conn->sslcrl && strlen(conn->sslcrl) > 0))
-		have_homedir = pqGetHomeDirectory(homedir, sizeof(homedir));
-	else	/* won't need it */
-		have_homedir = false;
-
-	/* Read the client certificate file */
-	if (conn->sslcert && strlen(conn->sslcert) > 0)
-		strncpy(fnbuf, conn->sslcert, sizeof(fnbuf));
-	else if (have_homedir)
-		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_CERT_FILE);
-	else
-		fnbuf[0] = '\0';
-
-	if (fnbuf[0] == '\0')
-	{
-		/* no home directory, proceed without a client cert */
-		have_cert = false;
-	}
-	else if (stat(fnbuf, &buf) != 0)
-	{
-		/*
-		 * If file is not present, just go on without a client cert; server
-		 * might or might not accept the connection.  Any other error,
-		 * however, is grounds for complaint.
-		 */
-		if (errno != ENOENT && errno != ENOTDIR)
-		{
-			printfPQExpBuffer(&conn->errorMessage,
-			   libpq_gettext("could not open certificate file \"%s\": %s\n"),
-							  fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
-			return -1;
-		}
-		have_cert = false;
-	}
-	else
+	char	   *sslcertfile = NULL;
+	char	   *engine = NULL;
+	char	   *keyname = NULL;
+	char	   *sslkeyfile = NULL;
+	char	   *sslrootcert = NULL;
+	char	   *sslcrl = NULL;
+	int			ret = -1;
+
+	if (!pqsecure_get_ssl_files(conn,
+								&sslcertfile, &sslkeyfile, &engine, &keyname,
+								&sslrootcert, &sslcrl))
+		return PGRES_POLLING_READING;
+
+	if (sslcertfile)
 	{
 		/*
 		 * Cert file exists, so load it.  Since OpenSSL doesn't provide the
@@ -855,216 +819,146 @@ initialize_SSL(PGconn *conn)
 		{
 			printfPQExpBuffer(&conn->errorMessage,
 			   libpq_gettext("could not acquire mutex: %s\n"), strerror(rc));
-			return -1;
+			goto fail;
 		}
 #endif
-		if (SSL_CTX_use_certificate_chain_file(SSL_context, fnbuf) != 1)
+		if (SSL_CTX_use_certificate_chain_file(SSL_context, sslcertfile) != 1)
 		{
 			char	   *err = SSLerrmessage();
 
 			printfPQExpBuffer(&conn->errorMessage,
 			   libpq_gettext("could not read certificate file \"%s\": %s\n"),
-							  fnbuf, err);
+							  sslcertfile, err);
 			SSLerrfree(err);
 
 #ifdef ENABLE_THREAD_SAFETY
 			pthread_mutex_unlock(&ssl_config_mutex);
 #endif
-			return -1;
+			goto fail;
 		}
 
-		if (SSL_use_certificate_file(conn->ssl, fnbuf, SSL_FILETYPE_PEM) != 1)
+		if (SSL_use_certificate_file(conn->ssl, sslcertfile, SSL_FILETYPE_PEM) != 1)
 		{
 			char	   *err = SSLerrmessage();
 
 			printfPQExpBuffer(&conn->errorMessage,
 			   libpq_gettext("could not read certificate file \"%s\": %s\n"),
-							  fnbuf, err);
+							  sslcertfile, err);
 			SSLerrfree(err);
 #ifdef ENABLE_THREAD_SAFETY
 			pthread_mutex_unlock(&ssl_config_mutex);
 #endif
-			return -1;
+			goto fail;
 		}
 
-		/* need to load the associated private key, too */
-		have_cert = true;
-
 #ifdef ENABLE_THREAD_SAFETY
 		pthread_mutex_unlock(&ssl_config_mutex);
 #endif
 	}
 
 	/*
-	 * Read the SSL key. If a key is specified, treat it as an engine:key
-	 * combination if there is colon present - we don't support files with
-	 * colon in the name. The exception is if the second character is a colon,
-	 * in which case it can be a Windows filename with drive specification.
+	 * If an engine:key specification was given, load that.
 	 */
-	if (have_cert && conn->sslkey && strlen(conn->sslkey) > 0)
+	if (engine)
 	{
 #ifdef USE_SSL_ENGINE
-		if (strchr(conn->sslkey, ':')
-#ifdef WIN32
-			&& conn->sslkey[1] != ':'
-#endif
-			)
+		conn->engine = ENGINE_by_id(engine);
+		if (conn->engine == NULL)
 		{
-			/* Colon, but not in second character, treat as engine:key */
-			char	   *engine_str = strdup(conn->sslkey);
-			char	   *engine_colon;
-
-			if (engine_str == NULL)
-			{
-				printfPQExpBuffer(&conn->errorMessage,
-								  libpq_gettext("out of memory\n"));
-				return -1;
-			}
-
-			/* cannot return NULL because we already checked before strdup */
-			engine_colon = strchr(engine_str, ':');
-
-			*engine_colon = '\0';		/* engine_str now has engine name */
-			engine_colon++;		/* engine_colon now has key name */
-
-			conn->engine = ENGINE_by_id(engine_str);
-			if (conn->engine == NULL)
-			{
-				char	   *err = SSLerrmessage();
+			char	   *err = SSLerrmessage();
 
-				printfPQExpBuffer(&conn->errorMessage,
+			printfPQExpBuffer(&conn->errorMessage,
 					 libpq_gettext("could not load SSL engine \"%s\": %s\n"),
-								  engine_str, err);
-				SSLerrfree(err);
-				free(engine_str);
-				return -1;
-			}
+							  engine, err);
+			SSLerrfree(err);
+			goto fail;
+		}
 
-			if (ENGINE_init(conn->engine) == 0)
-			{
-				char	   *err = SSLerrmessage();
+		if (ENGINE_init(conn->engine) == 0)
+		{
+			char	   *err = SSLerrmessage();
 
-				printfPQExpBuffer(&conn->errorMessage,
+			printfPQExpBuffer(&conn->errorMessage,
 				libpq_gettext("could not initialize SSL engine \"%s\": %s\n"),
-								  engine_str, err);
-				SSLerrfree(err);
-				ENGINE_free(conn->engine);
-				conn->engine = NULL;
-				free(engine_str);
-				return -1;
-			}
-
-			pkey = ENGINE_load_private_key(conn->engine, engine_colon,
-										   NULL, NULL);
-			if (pkey == NULL)
-			{
-				char	   *err = SSLerrmessage();
-
-				printfPQExpBuffer(&conn->errorMessage,
-								  libpq_gettext("could not read private SSL key \"%s\" from engine \"%s\": %s\n"),
-								  engine_colon, engine_str, err);
-				SSLerrfree(err);
-				ENGINE_finish(conn->engine);
-				ENGINE_free(conn->engine);
-				conn->engine = NULL;
-				free(engine_str);
-				return -1;
-			}
-			if (SSL_use_PrivateKey(conn->ssl, pkey) != 1)
-			{
-				char	   *err = SSLerrmessage();
-
-				printfPQExpBuffer(&conn->errorMessage,
-								  libpq_gettext("could not load private SSL key \"%s\" from engine \"%s\": %s\n"),
-								  engine_colon, engine_str, err);
-				SSLerrfree(err);
-				ENGINE_finish(conn->engine);
-				ENGINE_free(conn->engine);
-				conn->engine = NULL;
-				free(engine_str);
-				return -1;
-			}
-
-			free(engine_str);
-
-			fnbuf[0] = '\0';	/* indicate we're not going to load from a
-								 * file */
-		}
-		else
-#endif   /* USE_SSL_ENGINE */
-		{
-			/* PGSSLKEY is not an engine, treat it as a filename */
-			strncpy(fnbuf, conn->sslkey, sizeof(fnbuf));
+							  engine, err);
+			SSLerrfree(err);
+			ENGINE_free(conn->engine);
+			conn->engine = NULL;
+			goto fail;
 		}
-	}
-	else if (have_homedir)
-	{
-		/* No PGSSLKEY specified, load default file */
-		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_KEY_FILE);
-	}
-	else
-		fnbuf[0] = '\0';
 
-	if (have_cert && fnbuf[0] != '\0')
-	{
-		/* read the client key from file */
-
-		if (stat(fnbuf, &buf) != 0)
+		pkey = ENGINE_load_private_key(conn->engine, keyname, NULL, NULL);
+		if (pkey == NULL)
 		{
+			char	   *err = SSLerrmessage();
+
 			printfPQExpBuffer(&conn->errorMessage,
-							  libpq_gettext("certificate present, but not private key file \"%s\"\n"),
-							  fnbuf);
-			return -1;
+							  libpq_gettext("could not read private SSL key \"%s\" from engine \"%s\": %s\n"),
+							  keyname, engine, err);
+			SSLerrfree(err);
+			ENGINE_finish(conn->engine);
+			ENGINE_free(conn->engine);
+			conn->engine = NULL;
+			goto fail;
 		}
-#ifndef WIN32
-		if (!S_ISREG(buf.st_mode) || buf.st_mode & (S_IRWXG | S_IRWXO))
+		if (SSL_use_PrivateKey(conn->ssl, pkey) != 1)
 		{
+			char	   *err = SSLerrmessage();
+
 			printfPQExpBuffer(&conn->errorMessage,
-							  libpq_gettext("private key file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"),
-							  fnbuf);
-			return -1;
+							  libpq_gettext("could not load private SSL key \"%s\" from engine \"%s\": %s\n"),
+							  keyname, engine, err);
+			SSLerrfree(err);
+			ENGINE_finish(conn->engine);
+			ENGINE_free(conn->engine);
+			conn->engine = NULL;
+			goto fail;
 		}
-#endif
+#else
+		/*
+		 * should not happen; pqsecure_get_ssl_files doesn't return an
+		 * engine spec if not compiled with USE_SSL_ENGINE.
+		 */
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("engine not supported\n"));
+		goto fail;
+#endif   /* USE_SSL_ENGINE */
+	}
 
-		if (SSL_use_PrivateKey_file(conn->ssl, fnbuf, SSL_FILETYPE_PEM) != 1)
+	/* Read the client private key file */
+	if (sslkeyfile)
+	{
+		if (SSL_use_PrivateKey_file(conn->ssl, sslkeyfile, SSL_FILETYPE_PEM) != 1)
 		{
 			char	   *err = SSLerrmessage();
 
 			printfPQExpBuffer(&conn->errorMessage,
 			   libpq_gettext("could not load private key file \"%s\": %s\n"),
-							  fnbuf, err);
+							  sslkeyfile, err);
 			SSLerrfree(err);
-			return -1;
+			goto fail;
 		}
 	}
 
-	/* verify that the cert and key go together */
-	if (have_cert &&
+	/* Verify that the cert and key go together */
+	if (sslcertfile &&
 		SSL_check_private_key(conn->ssl) != 1)
 	{
 		char	   *err = SSLerrmessage();
 
 		printfPQExpBuffer(&conn->errorMessage,
 						  libpq_gettext("certificate does not match private key file \"%s\": %s\n"),
-						  fnbuf, err);
+						  sslkeyfile, err);
 		SSLerrfree(err);
-		return -1;
+		goto fail;
 	}
 
 	/*
-	 * If the root cert file exists, load it so we can perform certificate
-	 * verification. If sslmode is "verify-full" we will also do further
-	 * verification after the connection has been completed.
+	 * Load root cert, if exists. (pqsecure_get_ssl_files already complained
+	 * if no root cert was configured but sslmode requires verification
+	 * of server certificates.)
 	 */
-	if (conn->sslrootcert && strlen(conn->sslrootcert) > 0)
-		strncpy(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' &&
-		stat(fnbuf, &buf) == 0)
+	if (sslrootcert)
 	{
 		X509_STORE *cvstore;
 
@@ -1075,35 +969,28 @@ initialize_SSL(PGconn *conn)
 		{
 			printfPQExpBuffer(&conn->errorMessage,
 			   libpq_gettext("could not acquire mutex: %s\n"), strerror(rc));
-			return -1;
+			goto fail;
 		}
 #endif
-		if (SSL_CTX_load_verify_locations(SSL_context, fnbuf, NULL) != 1)
+		if (SSL_CTX_load_verify_locations(SSL_context, sslrootcert, NULL) != 1)
 		{
 			char	   *err = SSLerrmessage();
 
 			printfPQExpBuffer(&conn->errorMessage,
 							  libpq_gettext("could not read root certificate file \"%s\": %s\n"),
-							  fnbuf, err);
+							  sslrootcert, err);
 			SSLerrfree(err);
 #ifdef ENABLE_THREAD_SAFETY
 			pthread_mutex_unlock(&ssl_config_mutex);
 #endif
-			return -1;
+			goto fail;
 		}
 
 		if ((cvstore = SSL_CTX_get_cert_store(SSL_context)) != NULL)
 		{
-			if (conn->sslcrl && strlen(conn->sslcrl) > 0)
-				strncpy(fnbuf, conn->sslcrl, sizeof(fnbuf));
-			else if (have_homedir)
-				snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CRL_FILE);
-			else
-				fnbuf[0] = '\0';
-
 			/* Set the flags to check against the complete CRL chain */
-			if (fnbuf[0] != '\0' &&
-				X509_STORE_load_locations(cvstore, fnbuf, NULL) == 1)
+			if (sslcrl &&
+				X509_STORE_load_locations(cvstore, sslcrl, NULL) == 1)
 			{
 				/* OpenSSL 0.96 does not support X509_V_FLAG_CRL_CHECK */
 #ifdef X509_V_FLAG_CRL_CHECK
@@ -1114,12 +1001,12 @@ initialize_SSL(PGconn *conn)
 
 				printfPQExpBuffer(&conn->errorMessage,
 								  libpq_gettext("SSL library does not support CRL certificates (file \"%s\")\n"),
-								  fnbuf);
+								  sslcrl);
 				SSLerrfree(err);
 #ifdef ENABLE_THREAD_SAFETY
 				pthread_mutex_unlock(&ssl_config_mutex);
 #endif
-				return -1;
+				goto fail;
 #endif
 			}
 			/* if not found, silently ignore;  we do not require CRL */
@@ -1130,31 +1017,6 @@ initialize_SSL(PGconn *conn)
 
 		SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, verify_cb);
 	}
-	else
-	{
-		/*
-		 * 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 -1;
-		}
-	}
 
 	/*
 	 * If the OpenSSL version used supports it (from 1.0.0 on) and the user
@@ -1167,9 +1029,27 @@ initialize_SSL(PGconn *conn)
 	}
 #endif
 
-	return 0;
+	/* success */
+	ret = 0;
+
+fail:
+	if (sslcertfile)
+		free(sslcertfile);
+	if (engine)
+		free(engine);
+	if (keyname)
+		free(keyname);
+	if (sslkeyfile)
+		free(sslkeyfile);
+	if (sslrootcert)
+		free(sslrootcert);
+	if (sslcrl)
+		free(sslcrl);
+
+	return ret;
 }
 
+
 /*
  *	Attempt to negotiate SSL connection.
  */
diff --git a/src/interfaces/libpq/fe-secure-winschannel.c b/src/interfaces/libpq/fe-secure-winschannel.c
new file mode 100644
index 0000000..4af5936
--- /dev/null
+++ b/src/interfaces/libpq/fe-secure-winschannel.c
@@ -0,0 +1,1353 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-secure-winschannel.c
+ *	  Windows Schannel SSL implementation
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/interfaces/libpq/fe-secure-winschannel.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres_fe.h"
+
+#include <ctype.h>
+#include <fcntl.h>
+
+#include "libpq-fe.h"
+#include "libpq-int.h"
+
+#include "win32.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <sspi.h>
+#include <schnlsp.h>
+
+#define SCHANNEL_DEBUG
+
+typedef enum
+{
+	ISC_INIT,		/* before first call to InitializeSecurityContext */
+	ISC_NEED_READ,	/* need to pass more input to InitializeSecurityContext */
+	ISC_NEED_WRITE, /* need to write data to client */
+	ISC_FINAL_WRITE, /* handshake complete after sending last output to client */
+	ISC_COMPLETED	/* handshake complete */
+} HandshakeState;
+
+struct SchannelContext
+{
+	/* SChannel handle for the connection */
+	CtxtHandle  ctxHandle;
+	bool		ctxHandle_valid;
+	CredHandle	credHandle;
+	bool		credHandle_valid;
+
+	/* client certificate to send to server */
+	CERT_CONTEXT *clientCert;
+	/* Root certificate for validating server certificates */
+	CERT_CONTEXT *rootCert;
+
+	HandshakeState handshakeState;
+	bool		remote_shutdown;
+
+	/*
+	 * Buffer for encrypting data to be written.
+	 *
+	 * When encryptedLen > 0, the buffer contains some encrypted data that
+	 * should be written out to the client.
+	 *
+	 * When encryptedLen == 0, call encryptBuffer() to load the buffer with
+	 * more data, and encrypt it.
+	 */
+	char	   *encryptBuffer;
+	int			encryptBufferSize;
+	int			encryptBufferLen;
+
+	char	   *encryptedData;
+	size_t		encryptedLen;
+
+	/*
+	 * Number of plaintext bytes the buffer currently holds in encrypted form.
+	 * This is less than the number of encrypted bytes, because the SSL
+	 * protocol adds framing. (it could also be more, when compression is used)
+	 */
+	int			plaintextLen;
+
+	/*
+	 * Buffer for decrypting read data.
+	 *
+	 * decryptBufferSize is the allocated size of decryptBuffer.
+	 * decryptBufferLen is the number of ciphertext bytes loaded to the buffer.
+	 *
+	 * The buffer can be in one of two states. In the first state, it contains
+	 * zero or more raw bytes, and more can be loaded into it. In the other
+	 * state, it contains decrypted data that can be read out.
+	 *
+	 * When the buffer contains decrypted data, decryptedLen > 0, and
+	 * decryptedData points to the data. The buffer can also contain some
+	 * leftover raw data that has not been decrypted yet (extraData/extraLen).
+	 *
+	 * Before reading more raw data to the buffer, call
+	 * prepareDecryptBufferForRead. It moves any leftover raw data to the
+	 * beginning of the buffer.
+	 */
+	char	   *decryptBuffer;
+	int			decryptBufferSize;
+	int			decryptBufferLen; 	/* ciphertext bytes loaded in decryptBuffer */
+
+	/* decrypted data not returned to the caller yet */
+	char	   *decryptedData;
+	int			decryptedLen;
+
+	char	   *extraData;
+	int			extraLen;
+};
+
+typedef struct SchannelContext SchannelContext;
+
+/*
+ * Somewhat arbitrarily, use a 10k buffer for both input and output.
+ */
+#define BUFSIZE 10000
+
+/* Functions for managing the encrypt/decrypt buffers */
+static ssize_t encryptBuffer(PGconn *conn, const void *ptr, int len);
+static void prepareDecryptBufferForRead(SchannelContext *ctx);
+static int decryptBuffer(PGconn *conn);
+
+/* Functions for dealing with certificates */
+static bool loadCerts(PGconn *conn);
+static bool validateServerCert(PGconn *conn);
+
+/* Functions for converting error codes to strings */
+static const char *getSSLPolicyStatusCode(DWORD dwerror);
+static const char *getSecurityStatus(SECURITY_STATUS dwerror);
+
+void
+pgtls_init_library(bool do_ssl, int do_crypto)
+{
+}
+
+
+/*
+ * Initialize SSL system, in particular creating the SSL_context object
+ * that will be shared by all SSL-using connections in this process.
+ *
+ * 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)
+{
+	/* nothing to do here ATM */
+	return 0;
+}
+
+
+/*
+ *	Begin or continue negotiating a secure session.
+ */
+PostgresPollingStatusType
+pgtls_open_client(PGconn *conn)
+{
+	SchannelContext *ctx = conn->windows_schannel;
+	SecBufferDesc outsbufdesc;
+	SecBuffer	outsbufs[1];
+	SecBufferDesc insbufdesc;
+	SecBuffer	insbufs[2];
+	SECURITY_STATUS rc;
+	DWORD		flags;
+	DWORD		outflags;
+	int			n;
+
+#ifdef SCHANNEL_DEBUG
+	fprintf(stderr, "pgtls_open_client called: %d\n", ctx ? ctx->handshakeState : -1);
+	fflush(stderr);
+#endif
+
+	/* On first call, allocate the context struct and buffers */
+	if (ctx == NULL)
+	{
+		ctx = (SchannelContext *) malloc(sizeof(SchannelContext));
+		if (ctx == NULL)
+		{
+			pgtls_close(conn);
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("out of memory\n"));
+			return PGRES_POLLING_FAILED;
+		}
+		memset(ctx, 0, sizeof(SchannelContext));
+		conn->windows_schannel = ctx;
+
+		ctx->encryptBufferSize = BUFSIZE;
+		ctx->encryptBuffer = malloc(ctx->encryptBufferSize);
+		if (ctx->encryptBuffer == NULL)
+		{
+			pgtls_close(conn);
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("out of memory\n"));
+			return PGRES_POLLING_FAILED;
+		}
+
+		ctx->decryptBufferSize = BUFSIZE;
+		ctx->decryptBuffer = malloc(ctx->encryptBufferSize);
+		if (ctx->decryptBuffer == NULL)
+		{
+			pgtls_close(conn);
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("out of memory\n"));
+			return PGRES_POLLING_FAILED;
+		}
+
+		ctx->handshakeState = ISC_INIT;
+
+		conn->windows_schannel = ctx;
+		conn->ssl_in_use = true;
+	}
+
+	if (!ctx->credHandle_valid)
+	{
+		SCHANNEL_CRED credData;
+
+		if (!loadCerts(conn))
+		{
+			pgtls_close(conn);
+			return PGRES_POLLING_FAILED;
+		}
+
+		memset(&credData, 0, sizeof(credData));
+		credData.dwVersion = SCHANNEL_CRED_VERSION;
+		credData.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK |
+			SCH_CRED_NO_DEFAULT_CREDS |
+			SCH_CRED_MANUAL_CRED_VALIDATION;
+		if (ctx->clientCert)
+		{
+			credData.cCreds = 1;
+			credData.paCred = &ctx->clientCert;
+		}
+		else
+			credData.cCreds = 0;
+
+		/* FIXME: check server cert */
+
+#ifdef SCHANNEL_DEBUG
+		fprintf(stderr, "calling AcquireCredentialsHandle\n");
+		fflush(stderr);
+#endif
+		rc = AcquireCredentialsHandle(NULL,
+									  UNISP_NAME,
+									  SECPKG_CRED_OUTBOUND,
+									  NULL,
+									  &credData,
+									  NULL,
+									  NULL,
+									  &ctx->credHandle,
+									  NULL);
+		if (rc != SEC_E_OK)
+		{
+			pgtls_close(conn);
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("could not acquire credentials handle: %s\n"),
+							  getSecurityStatus(rc));
+			return PGRES_POLLING_FAILED;
+		}
+		ctx->credHandle_valid = true;
+
+#ifdef SCHANNEL_DEBUG
+		fprintf(stderr, "AcquireCredentialsHandle succeeded\n");
+		fflush(stderr);
+#endif
+	}
+
+	/* If we have any pending outgoing data, flush that first */
+	if (ctx->handshakeState == ISC_NEED_WRITE ||
+		ctx->handshakeState == ISC_FINAL_WRITE)
+	{
+#ifdef SCHANNEL_DEBUG
+		fprintf(stderr, "Sending %d handshake bytes\n",
+				ctx->encryptedLen);
+		fflush(stderr);
+#endif
+		n = pqsecure_raw_write(conn, ctx->encryptBuffer, ctx->encryptedLen);
+		if (n < 0)
+		{
+			int result_errno = SOCK_ERRNO;
+			if (result_errno == EINTR
+#ifdef EAGAIN
+				|| result_errno == EAGAIN
+#endif
+#ifdef EWOULDBLOCK
+				|| result_errno == EWOULDBLOCK)
+#endif
+			{
+				return PGRES_POLLING_WRITING;
+			}
+			else
+			{
+				pgtls_close(conn);
+				/* pqsecure_raw_write already set error message */
+				return PGRES_POLLING_FAILED;
+			}
+		}
+		ctx->encryptedData += n;
+		ctx->encryptedLen -= n;
+		if (ctx->encryptedLen > 0)
+			return PGRES_POLLING_WRITING;
+		else
+		{
+			if (ctx->handshakeState == ISC_NEED_WRITE)
+			{
+				/*
+				 * Successfully sent the data to client that
+				 * InitializeSecurityContext asked us to sent. Now wait for
+				 * a reply.
+				 */
+				ctx->handshakeState = ISC_NEED_READ;
+			}
+			else
+			{
+				ctx->handshakeState = ISC_COMPLETED;
+				return PGRES_POLLING_OK;
+			}
+		}
+	}
+
+	/* call InitializeSecurityContext */
+
+	flags = ISC_REQ_ALLOCATE_MEMORY |
+		ISC_REQ_CONFIDENTIALITY |
+		ISC_RET_EXTENDED_ERROR |
+		ISC_REQ_REPLAY_DETECT |
+		ISC_REQ_SEQUENCE_DETECT |
+		ISC_REQ_STREAM |
+		ISC_REQ_MANUAL_CRED_VALIDATION;
+
+	outsbufs[0].pvBuffer   = NULL;
+	outsbufs[0].BufferType = SECBUFFER_TOKEN;
+	outsbufs[0].cbBuffer   = 0;
+
+	outsbufdesc.cBuffers = 1;
+	outsbufdesc.pBuffers = outsbufs;
+	outsbufdesc.ulVersion = SECBUFFER_VERSION;
+
+	/* Read some data if needed */
+	if (ctx->handshakeState == ISC_NEED_READ)
+	{
+		int			maxsize;
+		int			readlen;
+
+		/* read more data to buffer */
+		maxsize = ctx->decryptBufferSize - ctx->decryptBufferLen;
+#ifdef SCHANNEL_DEBUG
+		fprintf(stderr, "reading maxsize %d buflen %d\n", maxsize, ctx->decryptBufferLen);
+		fflush(stderr);
+#endif
+		readlen = pqsecure_raw_read(conn,
+									&ctx->decryptBuffer[ctx->decryptBufferLen],
+									maxsize);
+		fprintf(stderr, "received %d bytes in handshake\n", readlen);
+		fflush(stderr);
+		if (readlen < 0)
+		{
+			int result_errno = SOCK_ERRNO;
+			if (result_errno == EINTR
+#ifdef EAGAIN
+				|| result_errno == EAGAIN
+#endif
+#ifdef EWOULDBLOCK
+				|| result_errno == EWOULDBLOCK)
+#endif
+			{
+				return PGRES_POLLING_READING;
+			}
+
+			pgtls_close(conn);
+			/* pqsecure_raw_read set error message */
+			return PGRES_POLLING_FAILED;
+		}
+
+		ctx->decryptBufferLen += readlen;
+	}
+
+	/*
+	 * On first call, don't pass input buffer. The function will return
+	 * a context. On subsequent calls, pass any input data in a buffer,
+	 * and pass the handle we got on first call.
+	 */
+	if (ctx->handshakeState == ISC_INIT)
+	{
+		rc = InitializeSecurityContext(&ctx->credHandle,
+									   NULL,
+									   NULL,
+									   flags,
+									   0,
+									   0,
+									   NULL,
+									   0,
+									   &ctx->ctxHandle,
+									   &outsbufdesc,
+									   &outflags,
+									   NULL);
+	}
+	else
+	{
+		insbufs[0].pvBuffer = ctx->decryptBuffer;
+		insbufs[0].cbBuffer = ctx->decryptBufferLen;
+		insbufs[0].BufferType = SECBUFFER_TOKEN;
+
+		insbufs[1].pvBuffer = NULL;
+		insbufs[1].cbBuffer = 0;
+		insbufs[1].BufferType = SECBUFFER_EMPTY;
+
+		insbufdesc.cBuffers = 2;
+		insbufdesc.pBuffers = insbufs;
+		insbufdesc.ulVersion = SECBUFFER_VERSION;
+
+		rc = InitializeSecurityContext(&ctx->credHandle,
+									   &ctx->ctxHandle,
+									   NULL,
+									   flags,
+									   0,
+									   0,
+									   &insbufdesc,
+									   0,
+									   NULL,
+									   &outsbufdesc,
+									   &outflags,
+									   NULL);
+		/*
+		 * The following return codes are considered success, in the sense
+		 * that a context was created and must eventually be destroyed to
+		 * avoid leaking memory.
+		 */
+		if (rc == SEC_I_COMPLETE_AND_CONTINUE ||
+			rc == SEC_I_COMPLETE_NEEDED ||
+			rc == SEC_I_CONTINUE_NEEDED ||
+			rc == SEC_I_INCOMPLETE_CREDENTIALS ||
+			rc == SEC_E_INCOMPLETE_MESSAGE ||
+			rc == SEC_E_OK)
+		{
+			ctx->ctxHandle_valid = true;
+		}
+	}
+#ifdef SCHANNEL_DEBUG
+	fprintf(stderr, "InitializeSecurityContext returned %x\n", (unsigned int) rc);
+	fflush(stderr);
+#endif
+
+	if (rc == SEC_E_OK || rc == SEC_I_CONTINUE_NEEDED)
+	{
+		size_t outLen;
+
+		/*
+		 * InitializeSecurityContext might not have consumed all of the input
+		 * data.
+		 */
+		if (ctx->handshakeState == ISC_NEED_READ)
+		{
+			if (insbufs[1].BufferType == SECBUFFER_EXTRA)
+			{
+				int extraLen = insbufs[1].cbBuffer;
+
+				memmove(ctx->decryptBuffer,
+						&ctx->decryptBuffer[ctx->decryptBufferLen - extraLen],
+						extraLen);
+				ctx->decryptBufferLen = extraLen;
+			}
+			else
+				ctx->decryptBufferLen = 0;
+		}
+
+		/*
+		 * Must send the token that InitializeSecurityContext put in the
+		 * output buffer (if any).
+		 */
+		outLen = outsbufs[0].cbBuffer;
+		if (outLen > 0)
+		{
+			/* enlarge the output buffer if required. */
+			if (outLen > ctx->encryptBufferSize)
+			{
+				char	   *newbuf;
+				newbuf = realloc(ctx->encryptBuffer, outsbufs[0].cbBuffer);
+				if (newbuf == NULL)
+				{
+					pgtls_close(conn);
+					printfPQExpBuffer(&conn->errorMessage,
+									  libpq_gettext("out of memory\n"));
+					return PGRES_POLLING_FAILED;
+				}
+				ctx->encryptBuffer = newbuf;
+				ctx->encryptBufferSize = outLen;
+			}
+			memcpy(ctx->encryptBuffer, outsbufs[0].pvBuffer, outLen);
+			ctx->encryptedData = ctx->encryptBuffer;
+			ctx->encryptedLen = outLen;
+
+			FreeContextBuffer(outsbufs[0].pvBuffer);
+		}
+	}
+
+	switch(rc)
+	{
+		case SEC_E_OK:
+			/*
+			 * SSL handshake is complete. But we might still have some
+			 * data pending in the output buffer.
+			 */
+			if (!validateServerCert(conn))
+			{
+				pgtls_close(conn);
+				return PGRES_POLLING_FAILED;
+			}
+
+			if (ctx->encryptedLen > 0)
+			{
+				ctx->handshakeState = ISC_FINAL_WRITE;
+				return PGRES_POLLING_WRITING;
+			}
+
+			return PGRES_POLLING_OK;
+
+		case SEC_I_COMPLETE_AND_CONTINUE:
+			pgtls_close(conn);
+			printfPQExpBuffer(&conn->errorMessage,
+							  "SEC_I_COMPLETE_AND_CONTINUE not implemented\n", rc);
+			return PGRES_POLLING_FAILED;
+
+		case SEC_I_COMPLETE_NEEDED:
+			pgtls_close(conn);
+			printfPQExpBuffer(&conn->errorMessage,
+							  "SEC_I_COMPLETE_NEEDED not implemented\n", rc);
+			return PGRES_POLLING_FAILED;
+
+		case SEC_I_CONTINUE_NEEDED:
+			/*
+			 * Write the output data that InitializeSecurityContext returned,
+			 * and wait for reply.
+			 */
+			ctx->handshakeState = ISC_NEED_WRITE;
+			return PGRES_POLLING_WRITING;
+
+		case SEC_I_INCOMPLETE_CREDENTIALS:
+			pgtls_close(conn);
+			printfPQExpBuffer(&conn->errorMessage,
+							  "SEC_I_INCOMPLETE_CREDENTIALS not implemented\n", rc);
+			return PGRES_POLLING_FAILED;
+
+		case SEC_E_INCOMPLETE_MESSAGE:
+			/* read more input data and retry */
+			ctx->handshakeState = ISC_NEED_READ;
+			return PGRES_POLLING_READING;
+
+		default:
+			pgtls_close(conn);
+			printfPQExpBuffer(&conn->errorMessage,
+							  "InitializeSecurityContext failed: %s\n",
+							  getSecurityStatus(rc));
+			return PGRES_POLLING_FAILED;
+	}
+	/* not reached */
+
+fail:
+	pgtls_close(conn);
+	return PGRES_POLLING_FAILED;
+}
+
+/*
+ *	Close SSL connection.
+ */
+void
+pgtls_close(PGconn *conn)
+{
+	SchannelContext *ctx = conn->windows_schannel;
+
+	/* TODO: should do a proper SSL shutdown. */
+
+	if (ctx)
+	{
+		if (ctx->encryptBuffer)
+			free(ctx->encryptBuffer);
+		if (ctx->decryptBuffer)
+			free(ctx->decryptBuffer);
+
+		if (ctx->credHandle_valid)
+			FreeCredentialsHandle(&ctx->credHandle);
+
+		if (ctx->ctxHandle_valid)
+			DeleteSecurityContext(&ctx->ctxHandle);
+
+		free(ctx);
+		conn->windows_schannel = NULL;
+	}
+	conn->ssl_in_use = false;
+}
+
+/*
+ *	Read data from a secure connection.
+ *
+ * On failure, this function is responsible for putting a suitable message
+ * into conn->errorMessage.  The caller must still inspect errno, but only
+ * to determine whether to continue/retry after error.
+ */
+ssize_t
+pgtls_read(PGconn *conn, char *buffer, size_t len)
+{
+	SchannelContext *ctx = conn->windows_schannel;
+	int			readlen;
+	int			maxsize;
+	size_t		totalreadlen = 0;
+	char	   *dst = buffer;
+
+	for (;;)
+	{
+		/* if we already have some decrypted data, copy it to the target buffer */
+		if (ctx->decryptedLen > 0)
+		{
+			maxsize = len - totalreadlen;
+			if (maxsize > ctx->decryptedLen)
+				maxsize = ctx->decryptedLen;
+			memcpy(dst, &ctx->decryptedData[totalreadlen], maxsize);
+			dst += maxsize;
+			totalreadlen += maxsize;
+			ctx->decryptedLen -= maxsize;
+			ctx->decryptedData += maxsize;
+		}
+
+		/* If we have some data to return, return it. */
+		if (totalreadlen > 0)
+		{
+			return totalreadlen;
+		}
+
+		/*
+		 * there shouldn't be any decrypted data left in the buffer, we copied
+		 * it all to the target.
+		 */
+		Assert(ctx->decryptedLen == 0);
+
+		/*
+		 * Need to decrypt more data, and for that, need to read more raw
+		 * data.
+		 */
+		prepareDecryptBufferForRead(ctx);
+		while (ctx->decryptedLen == 0)
+		{
+			/* read more data to buffer */
+			maxsize = ctx->decryptBufferSize - ctx->decryptBufferLen;
+			readlen = pqsecure_raw_read(conn,
+										&ctx->decryptBuffer[ctx->decryptBufferLen],
+										maxsize);
+			if (readlen < 0)
+				return readlen;
+			if (readlen == 0)
+			{
+				/* this implies we're in non-blocking mode */
+				return totalreadlen;
+			}
+
+			ctx->decryptBufferLen += readlen;
+
+			/* decrypt what we got */
+			if (decryptBuffer(conn) == -1)
+				return -1;
+			if (ctx->remote_shutdown)
+				return totalreadlen;
+
+			/* If decryptBuffer needs more data, decrypteLen is still 0. */
+		}
+	}
+
+	return totalreadlen;
+}
+
+
+/*
+ * Prepare the buffer for reading in more cipher text.
+ */
+static void
+prepareDecryptBufferForRead(SchannelContext *ctx)
+{
+	/*
+	 * should've consumed all the previous decrypted data in the buffer befor
+	 * decrypting more.
+	 */
+	Assert(ctx->decryptedLen == 0);
+
+	/*
+	 * If there's any ciphertext left in the buffer from previous round, move
+	 * it to beginning.
+	 */
+	if (ctx->extraLen > 0)
+		memmove(ctx->decryptBuffer,	ctx->extraData,	ctx->extraLen);
+	ctx->decryptBufferLen = ctx->extraLen;
+	ctx->extraData = NULL;
+	ctx->extraLen = 0;
+	ctx->decryptedData = NULL;
+	ctx->decryptedLen = 0;
+}
+
+/*
+ * Try to decrypt the data currently in decryptBuffer. On success,
+ * decryptedData points to the decrypted data. DecryptMessage decrypts
+ * in-place, so decryptedData points to somewhere within the buffer!
+ *
+ * On success, returns 1. On error, returns -1. If more raw data is needed
+ * to decrypt, returns 0. If the client shut down the connection, also
+ * returns 0, and sets ctx->remote_shutdown flag.
+ */
+static int
+decryptBuffer(PGconn *conn)
+{
+	SchannelContext *ctx = conn->windows_schannel;
+	/*
+	 * Call DecryptBuffer until it returns some data, fails, or indicates that
+	 * it needs more input
+	 */
+	while(ctx->decryptedLen == 0)
+	{
+		SecBufferDesc sbufdesc;
+		SecBuffer	sbufs[4];
+		SECURITY_STATUS rc;
+		int			i;
+
+		Assert(ctx->decryptBufferLen > 0);
+		Assert(ctx->extraLen == 0);
+
+		sbufs[0].pvBuffer = ctx->decryptBuffer;
+		sbufs[0].cbBuffer = ctx->decryptBufferLen;
+		sbufs[0].BufferType = SECBUFFER_DATA;
+		sbufs[1].pvBuffer = NULL;
+		sbufs[1].cbBuffer = 0;
+		sbufs[1].BufferType   = SECBUFFER_EMPTY;
+		sbufs[2].pvBuffer = NULL;
+		sbufs[2].cbBuffer = 0;
+		sbufs[2].BufferType   = SECBUFFER_EMPTY;
+		sbufs[3].pvBuffer = NULL;
+		sbufs[3].cbBuffer = 0;
+		sbufs[3].BufferType   = SECBUFFER_EMPTY;
+
+		sbufdesc.ulVersion = SECBUFFER_VERSION;
+		sbufdesc.cBuffers = 4;
+		sbufdesc.pBuffers = sbufs;
+
+		rc = DecryptMessage(&ctx->ctxHandle, &sbufdesc, 0, NULL);
+
+		if (rc == SEC_E_INCOMPLETE_MESSAGE)
+		{
+			/* We don't have the full message yet. Need more data. */
+			return 0;
+		}
+		if (rc == SEC_I_CONTEXT_EXPIRED)
+		{
+			ctx->remote_shutdown = true;
+			return 0;
+		}
+		if (rc == SEC_I_RENEGOTIATE)
+		{
+			/* TODO */
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("SSL renegotiation not implemented\n\n"));
+			return -1;
+		}
+		if (rc != SEC_E_OK)
+		{
+			/* FIXME: set errno */
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("could not decrypt received data: %s\n"),
+							  getSecurityStatus(rc));
+			return -1;
+		}
+
+		for (i = 0; i < 4; i++)
+		{
+			if (sbufs[i].BufferType == SECBUFFER_EXTRA)
+			{
+				ctx->extraData = sbufs[i].pvBuffer;
+				ctx->extraLen = sbufs[i].cbBuffer;
+			}
+			if (sbufs[i].BufferType == SECBUFFER_DATA)
+			{
+				ctx->decryptedData = sbufs[i].pvBuffer;
+				ctx->decryptedLen = sbufs[i].cbBuffer;
+			}
+		}
+		if (ctx->decryptedLen == 0)
+		{
+			/*
+			 * DecryptMessage didn't decrypt anything. Shift the extra data
+			 * to beginning of buffer, and retry.
+			 */
+			prepareDecryptBufferForRead(ctx);
+		}
+	}
+
+	return 1;
+}
+
+/*
+ *	Write data to a secure connection.
+ *
+ * On failure, this function is responsible for putting a suitable message
+ * into conn->errorMessage.  The caller must still inspect errno, but only
+ * to determine whether to continue/retry after error.
+ */
+ssize_t
+pgtls_write(PGconn *conn, const void *ptr, size_t len)
+{
+	SchannelContext *ctx = conn->windows_schannel;
+	int			sendlen;
+
+	/*
+	 * If the output buffer is empty, encrypt (some of) the input data to fill
+	 * it.
+	 */
+	if (ctx->encryptedLen == 0)
+	{
+		int			plaintextLen;
+
+		plaintextLen = encryptBuffer(conn, ptr, len);
+		if (plaintextLen < 0)
+			return -1;
+		ctx->plaintextLen = plaintextLen;
+	}
+
+	/*
+	 * Write out the encrypted data in the buffer
+	 */
+	sendlen = pqsecure_raw_write(conn,
+								 ctx->encryptedData,
+								 ctx->encryptedLen);
+	if (sendlen <= 0)
+		return sendlen;
+
+	ctx->encryptedData += sendlen;
+	ctx->encryptedLen -= sendlen;
+
+	if (ctx->encryptedLen == 0)
+	{
+		/* sent all the data in the buffer. */
+		return ctx->plaintextLen;
+	}
+	else
+	{
+		/* return 0 to indicate that the caller has to retry. */
+		return 0;
+	}
+}
+
+
+/*
+ * Encrypt more data. Helper function for pgtls_write().
+ */
+static ssize_t
+encryptBuffer(PGconn *conn, const void *ptr, int len)
+{
+	SchannelContext *ctx = conn->windows_schannel;
+	SecPkgContext_StreamSizes sizes;
+	SecBufferDesc sbufdesc;
+	SecBuffer	sbufs[4];
+	SECURITY_STATUS rc;
+
+	/* we should've sent out all encrypted data from previous round first */
+	Assert(ctx->encryptedLen == 0);
+
+	rc = QueryContextAttributes(&ctx->ctxHandle,
+								SECPKG_ATTR_STREAM_SIZES, &sizes);
+	if (rc != SEC_E_OK)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("QueryContextAttributes failed: %x"),
+						  rc);
+		return -1;
+	}
+	if (sizes.cbHeader + sizes.cbTrailer + len > ctx->encryptBufferSize)
+		len = ctx->encryptBufferSize - (sizes.cbHeader + sizes.cbTrailer);
+
+	memcpy(&ctx->encryptBuffer[sizes.cbHeader], ptr, len);
+
+	sbufs[0].pvBuffer = ctx->encryptBuffer;
+	sbufs[0].cbBuffer = sizes.cbHeader;
+	sbufs[0].BufferType = SECBUFFER_STREAM_HEADER;
+
+	sbufs[1].pvBuffer = &ctx->encryptBuffer[sizes.cbHeader];
+	sbufs[1].cbBuffer = len;
+	sbufs[1].BufferType = SECBUFFER_DATA;
+
+	sbufs[2].pvBuffer = &ctx->encryptBuffer[sizes.cbHeader + len];
+	sbufs[2].cbBuffer = sizes.cbTrailer;
+	sbufs[2].BufferType = SECBUFFER_STREAM_TRAILER;
+
+	sbufs[3].pvBuffer = NULL;
+	sbufs[3].cbBuffer = 0;
+	sbufs[3].BufferType = SECBUFFER_EMPTY;
+
+	sbufdesc.ulVersion = SECBUFFER_VERSION;
+	sbufdesc.cBuffers = 4;
+	sbufdesc.pBuffers = sbufs;
+
+	rc = EncryptMessage(&ctx->ctxHandle, 0, &sbufdesc, 0);
+	if (rc != SEC_E_OK)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("EncryptMessage failed: %x"),
+						  rc);
+		return -1;
+	}
+
+	ctx->encryptedData = ctx->encryptBuffer;
+	ctx->encryptedLen = sbufs[0].cbBuffer + sbufs[1].cbBuffer + sbufs[2].cbBuffer;
+
+	return len;
+}
+
+/**** Certificate handling functions ****/
+
+static bool
+validateServerCert(PGconn *conn)
+{
+	SchannelContext *ctx = conn->windows_schannel;
+	CERT_CONTEXT *remoteCert;
+	SECURITY_STATUS rc;
+	DWORD flags;
+
+	/* Get server certificate */
+	rc = QueryContextAttributes(&ctx->ctxHandle,
+								SECPKG_ATTR_REMOTE_CERT_CONTEXT,
+								(PVOID) &remoteCert);
+	if (rc != SEC_E_OK)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("could not get server certificate: %s\n"),
+						  getSecurityStatus(rc));
+		return false;
+	}
+
+	/*
+	 * Per MSDN documentation [1], we must check the following things:
+	 *
+	 * 1. The certificate chain is complete and the root is a certificate from
+	 *    a trusted certification authority (CA).
+	 * 2. The current time is not beyond the begin and end dates for each of
+	 *    the certificates in the certificate chain.
+	 * 3. None of the certificates in the certificate chain have been revoked.
+	 * 4. The depth of the leaf certificate is not deeper than the maximum
+	 *    allowable depth specified in the certificate extension. This check is
+	 *    only necessary if there is a depth specified.
+	 * 5. The usage of the certificate is correct, for example, a client
+	 *    certificate should not be used to authenticate a server.
+	 *
+	 * [1] "Manually Validating Schannel Credential",
+	 *     http://msdn.microsoft.com/en-us/library/windows/desktop/aa378740%28v=vs.85%29.aspx
+	 */
+
+	/*
+	 * 1. Check the chain
+	 * 2. Check expiration
+	 *
+	 * TODO: Currently, we only support client certs signed by the root client
+	 * CA. Intermediary CA's are not supported.
+	 */
+
+	/* TODO: CRL's are not actually supported */
+	flags = CERT_STORE_SIGNATURE_FLAG |
+		CERT_STORE_TIME_VALIDITY_FLAG;
+	if (!CertVerifySubjectCertificateContext(
+			remoteCert,
+			ctx->rootCert,
+			&flags))
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+					  libpq_gettext("could not verify server certificate\n"));
+		return false;
+	}
+	if (flags != 0)
+	{
+		if ((flags & CERT_STORE_SIGNATURE_FLAG) != 0)
+			printfPQExpBuffer(&conn->errorMessage,
+					  libpq_gettext("signature check failed on client cert\n"));
+		else if ((flags & CERT_STORE_REVOCATION_FLAG) != 0)
+			printfPQExpBuffer(&conn->errorMessage,
+					  libpq_gettext("server certificate was revoked\n"));
+		else if ((flags & CERT_STORE_TIME_VALIDITY_FLAG) != 0)
+			printfPQExpBuffer(&conn->errorMessage,
+					  libpq_gettext("server certificate is not valid at this time\n"));
+		else
+			printfPQExpBuffer(&conn->errorMessage,
+					  libpq_gettext("server certificate is not valid\n"));
+
+		return false;
+	}
+
+	/*
+	 * We succeeded to get the issuer's certificate from the trusted root store,
+	 * and the signature on the client cert passed, so we're good to go.
+	 */
+
+	return true;
+}
+
+
+/*
+ * Loads the certificate into a Certificate Context.
+ *
+ * on error, sets an error message in conn and returns NULL.
+ */
+static CERT_CONTEXT *
+loadCert(PGconn *conn, char *path)
+{
+	CERT_CONTEXT *cert_cxt;
+	char		crtBuf[50000]; /* TODO: enlarge as needed */
+	DWORD		crtBufLen = 50000;
+	struct stat statbuf;
+	FILE	   *fp;
+	char	   *content;
+	int			r;
+	int			filelen;
+
+	/* Read file into memory */
+	if (stat(path, &statbuf) != 0)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+					  libpq_gettext("could not stat certificate file \"%s\""),
+						  path);
+		return NULL;
+	}
+	filelen = statbuf.st_size;
+
+	fp = fopen(path, "rb");
+	if (!fp)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+					  libpq_gettext("could not open certificate file \"%s\""),
+						  path);
+		return NULL;
+	}
+	content = malloc(filelen);
+	r = fread(content, filelen, 1, fp);
+	if (r != 1 || ferror(fp) || fclose(fp))
+	{
+		free(content);
+		printfPQExpBuffer(&conn->errorMessage,
+					  libpq_gettext("could not read certificate file \"%s\""),
+						  path);
+		return NULL;
+	}
+
+	/* convert from PEM to DER */
+	if (!CryptStringToBinary(
+			content,
+			filelen,
+			CRYPT_STRING_BASE64HEADER,
+			crtBuf,
+			&crtBufLen,
+			NULL,
+			NULL))
+	{
+		free(content);
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("could not convert certificate file \"%s\" from PEM format"),
+						  path);
+		return NULL;
+	}
+	if (crtBufLen > sizeof(crtBuf))
+	{
+		/* TODO: enlarge buffer */
+		free(content);
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("out of memory"));
+		return NULL;
+	}
+
+	cert_cxt = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+											crtBuf,
+											crtBufLen);
+	if (cert_cxt == NULL)
+	{
+		free(content);
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("could not create certificate context"));
+		return NULL;
+	}
+
+	return cert_cxt;
+
+}
+
+static bool
+loadPrivateKey(PGconn *conn, CERT_CONTEXT *cert, char *path)
+{
+	char		keyBuf[50000]; /* TODO: enlarge as needed */
+	DWORD		keyBufLen = 50000;
+	CERT_KEY_PROV_INFO key;
+	HCRYPTPROV	cryptProvHandle;
+	HCRYPTKEY	keyHandle;
+	PUBLICKEYSTRUC *blob;
+	struct stat statbuf;
+	FILE	   *fp;
+	char	   *content;
+	int			r;
+	int			filelen;
+	char		privKeyBlob[50000];
+	int			privKeyBlobLen = 50000;
+
+	/* Read file into memory */
+	if (stat(path, &statbuf) != 0)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+					  libpq_gettext("could not read private key file \"%s\""),
+						  path);
+		return false;
+	}
+	filelen = statbuf.st_size;
+
+	fp = fopen(path, "rb");
+	if (!fp)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+					  libpq_gettext("could not read private key file \"%s\""),
+						  path);
+		return false;
+	}
+	content = malloc(filelen);
+	r = fread(content, filelen, 1, fp);
+	if (r != 1 || ferror(fp) || fclose(fp))
+	{
+		free(content);
+		printfPQExpBuffer(&conn->errorMessage,
+					  libpq_gettext("could not read private key file \"%s\""),
+						  path);
+		return false;
+	}
+
+	/* convert from PEM to DER */
+	if (!CryptStringToBinary(
+			content,
+			filelen,
+			CRYPT_STRING_BASE64HEADER,
+			keyBuf,
+			&keyBufLen,
+			NULL,
+			NULL))
+	{
+		free(content);
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("could not convert private key file \"%s\" from PEM format"),
+						  path);
+		return false;
+	}
+	if (keyBufLen > sizeof(keyBuf))
+	{
+		/* TODO: enlarge buffer */
+		free(content);
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("out of memory"));
+		return false;
+	}
+
+	/*
+	 * Load the key.
+	 */
+	if (!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+							 PKCS_RSA_PRIVATE_KEY,
+							 keyBuf,
+							 keyBufLen,
+							 0,
+							 NULL,
+							 privKeyBlob,
+							 &privKeyBlobLen))
+	{
+		free(content);
+		printfPQExpBuffer(&conn->errorMessage,
+					  libpq_gettext("could not parse private key file \"%s\""),
+						  path);
+		return false;
+	}
+	blob = (PUBLICKEYSTRUC *) privKeyBlob;
+
+	if (!CryptAcquireContext(&cryptProvHandle, "mycontainer", MS_ENHANCED_PROV, PROV_RSA_FULL, 0))
+	{
+		free(content);
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("CryptAcquireContext failed"));
+		return false;
+	}
+
+	if (!CryptImportKey(cryptProvHandle, privKeyBlob, privKeyBlobLen,
+						(HCRYPTKEY) NULL, 0, &keyHandle))
+	{
+		free(content);
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("CryptAcquireContext failed"));
+		return false;
+	}
+
+	memset(&keyCtx, 0, sizeof(keyCtx));
+	keyCtx.cbSize = sizeof(keyCtx);
+	keyCtx.hCryptProv = cryptProvHandle;
+	keyCtx.dwKeySpec = AT_KEYEXCHANGE;
+
+	if (!CertSetCertificateContextProperty(cert,
+										   CERT_KEY_CONTEXT_PROP_ID,
+										   0,
+										   &keyCtx))
+	{
+		free(content);
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("CertSetCertificateContextProperty failed"));
+		return false;
+	}
+
+	return true;
+}
+
+static bool
+loadCerts(PGconn *conn)
+{
+	SchannelContext *ctx = conn->windows_schannel;
+	char	   *sslcertfile = NULL;
+	char	   *engine = NULL;
+	char	   *keyname = NULL;
+	char	   *sslkeyfile = NULL;
+	char	   *sslrootcert = NULL;
+	char	   *sslcrl = NULL;
+	bool		ret = false;
+
+	if (!pqsecure_get_ssl_files(conn, &sslcertfile, &engine, &keyname, &sslkeyfile,
+								&sslrootcert, &sslcrl))
+		return false;
+
+#ifdef SCHANNEL_DEBUG
+	fprintf(stderr, "files: %s %s %s\n",
+			sslcertfile ? sslcertfile : "<no client cert>",
+			sslkeyfile ? sslkeyfile : "<no client key>",
+			sslrootcert ? sslrootcert : "<no root cert>",
+			sslcrl ? sslcrl : "<no crl>");
+	fflush(stderr);
+#endif
+
+	if (engine || keyname)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("engines not supported"));
+		goto fail;
+	}
+
+	if (sslcertfile)
+	{
+		ctx->clientCert = loadCert(conn, sslcertfile);
+		if (ctx->clientCert == NULL)
+			goto fail;
+
+		/*
+		 * Cool, got the client cert. Also load the private key associated
+		 * with it.
+		 */
+		if (!loadPrivateKey(conn, ctx->clientCert, sslkeyfile))
+			goto fail;
+	}
+
+	/* Load root certificate for validating server certificates */
+	if (sslrootcert)
+	{
+		ctx->rootCert = loadCert(conn, sslrootcert);
+		if (ctx->rootCert == NULL)
+			goto fail;
+	}
+
+	ret = true;
+
+fail:
+	if (sslcertfile)
+		free(sslcertfile);
+	if (engine)
+		free(engine);
+	if (keyname)
+		free(keyname);
+	if (sslkeyfile)
+		free(sslkeyfile);
+	if (sslrootcert)
+		free(sslrootcert);
+	if (sslcrl)
+		free(sslcrl);
+
+#ifdef SCHANNEL_DEBUG
+	fprintf(stderr, "certs loaded: %d\n", ret);
+	fflush(stderr);
+#endif
+
+	return ret;
+}
+
+
+/*
+ *	TODO: need a new API for this.
+ */
+void *
+PQgetssl(PGconn *conn)
+{
+	/*
+	 * return non-zero, so that psql reports that SSL is used. Of course, it's
+	 * totally bogus for anyone who actually tries to get the OpenSSL
+	 * SSL struct.
+	 */
+	return (void *) 1;
+}
+
+/*** Printing error codes ***/
+
+#define ERRORCODE_STR(x) { x, #x }
+
+typedef struct
+{
+	uint32 rc;
+	const char *str;
+} errorcode_str;
+
+static const char *
+getErrorStr(uint32 rc, errorcode_str *map)
+{
+	static char buf[100];
+	int i;
+
+	for (i = 0; map[i].str != NULL; i++)
+	{
+		if (map[i].rc == rc)
+			return map[i].str;
+	}
+	snprintf(buf, sizeof(buf), "0x%X", rc);
+	return buf;
+}
+
+static const char *
+getSecurityStatus(SECURITY_STATUS rc)
+{
+	static errorcode_str sec_status_codes[] = {
+		ERRORCODE_STR(SEC_E_OK),
+		ERRORCODE_STR(SEC_E_INCOMPLETE_MESSAGE),
+		ERRORCODE_STR(SEC_E_INSUFFICIENT_MEMORY),
+		ERRORCODE_STR(SEC_E_INTERNAL_ERROR),
+		ERRORCODE_STR(SEC_E_INVALID_HANDLE),
+		ERRORCODE_STR(SEC_E_INVALID_TOKEN),
+		ERRORCODE_STR(SEC_E_LOGON_DENIED),
+		ERRORCODE_STR(SEC_E_NO_AUTHENTICATING_AUTHORITY),
+		ERRORCODE_STR(SEC_E_NO_CREDENTIALS),
+		ERRORCODE_STR(SEC_I_COMPLETE_AND_CONTINUE),
+		ERRORCODE_STR(SEC_I_COMPLETE_NEEDED),
+		ERRORCODE_STR(SEC_I_CONTINUE_NEEDED),
+
+		ERRORCODE_STR(SEC_E_ALGORITHM_MISMATCH),
+		ERRORCODE_STR(SEC_E_UNSUPPORTED_FUNCTION),
+		ERRORCODE_STR(SEC_E_UNTRUSTED_ROOT),
+		{ 0, NULL }
+	};
+	return getErrorStr(rc, sec_status_codes);
+}
diff --git a/src/interfaces/libpq/fe-secure.c b/src/interfaces/libpq/fe-secure.c
index 66778b2..e19fc61 100644
--- a/src/interfaces/libpq/fe-secure.c
+++ b/src/interfaces/libpq/fe-secure.c
@@ -475,3 +475,297 @@ pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending, bool got_epipe)
 }
 
 #endif   /* ENABLE_THREAD_SAFETY && !WIN32 */
+
+/*
+ *	Determine the filenames of SSL related files.
+ *
+ * The filenames returned are checked to exist; the caller should throw an
+ * error if one of the returned files can not be opened. This function
+ * performs some preliminariry sanity checks, e.g. if a certificate file
+ * exists, we must also find the corresponding private key file.
+ *
+ * *sslcert is set to the path of the client's certificate, and *sslkey to
+ * the path to the corresponding private key.  If compiled with
+ * USE_SSL_ENGINE, a pathname containing a colon is interpreted as a
+ * a two-part "engin:key" string, for specifying a hardware key.
+ *
+ * *sslrootcert and *sslcrl are set to the paths to CA certificate and
+ * CRL files, used to validate the server certificate.
+ *
+ * The returned strings are malloc'd, and the caller is responsible for
+ * freeing them. On error, returns false and sets the libpq error message.
+ */
+bool
+pqsecure_get_ssl_files(PGconn *conn, char **sslcert, char **sslkey,
+					   char **engine, char **keyname,
+					   char **sslrootcert, char **sslcrl)
+{
+	struct stat buf;
+	char		homedir[MAXPGPATH];
+	char		fnbuf[MAXPGPATH];
+	char		sebuf[256];
+	bool		have_homedir;
+
+	*sslcert = NULL;
+	*sslkey = NULL;
+	*engine = NULL;
+	*keyname = NULL;
+	*sslrootcert = NULL;
+	*sslcrl = NULL;
+
+	/*
+	 * We'll need the home directory if any of the relevant parameters are
+	 * defaulted.  If pqGetHomeDirectory fails, act as though none of the
+	 * files could be found.
+	 */
+	if (!(conn->sslcert && strlen(conn->sslcert) > 0) ||
+		!(conn->sslkey && strlen(conn->sslkey) > 0) ||
+		!(conn->sslrootcert && strlen(conn->sslrootcert) > 0) ||
+		!(conn->sslcrl && strlen(conn->sslcrl) > 0))
+		have_homedir = pqGetHomeDirectory(homedir, sizeof(homedir));
+	else	/* won't need it */
+		have_homedir = false;
+
+	/* Find the client certificate file */
+	if (conn->sslcert && strlen(conn->sslcert) > 0)
+		strncpy(fnbuf, conn->sslcert, sizeof(fnbuf));
+	else if (have_homedir)
+		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_CERT_FILE);
+	else
+		fnbuf[0] = '\0';
+
+	if (fnbuf[0] == '\0')
+	{
+		/* no home directory, proceed without a client cert */
+	}
+	else if (stat(fnbuf, &buf) != 0)
+	{
+		/*
+		 * If file is not present, just go on without a client cert; server
+		 * might or might not accept the connection.  Any other error,
+		 * however, is grounds for complaint.
+		 */
+		if (errno != ENOENT && errno != ENOTDIR)
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+			   libpq_gettext("could not open certificate file \"%s\": %s\n"),
+							  fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
+			goto fail;
+		}
+	}
+	else
+	{
+		*sslcert = strdup(fnbuf);
+		if (*sslcert == NULL)
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("out of memory\n"));
+			goto fail;
+		}
+	}
+
+	/*
+	 * Read the SSL key. If a key is specified, treat it as an engine:key
+	 * combination if there is colon present - we don't support files with
+	 * colon in the name. The exception is if the second character is a colon,
+	 * in which case it can be a Windows filename with drive specification.
+	 */
+	if (*sslcert && conn->sslkey && strlen(conn->sslkey) > 0)
+	{
+#ifdef USE_SSL_ENGINE
+		if (strchr(conn->sslkey, ':')
+#ifdef WIN32
+			&& conn->sslkey[1] != ':'
+#endif
+			)
+		{
+			/* Colon, but not in second character, treat as engine:key */
+			char	   *engine_str = strdup(conn->sslkey);
+			char	   *engine_colon;
+
+			if (engine_str == NULL)
+			{
+				printfPQExpBuffer(&conn->errorMessage,
+								  libpq_gettext("out of memory\n"));
+				goto fail;
+			}
+
+			/* cannot return NULL because we already checked before strdup */
+			engine_colon = strchr(engine_str, ':');
+
+			*engine_colon = '\0';		/* engine_str now has engine name */
+			engine_colon++;		/* engine_colon now has key name */
+
+			*engine = strdup(engine_str);
+			if (*engine)
+				*keyname = strdup(engine_colon);
+
+			free(engine_str);
+
+			if (*engine == NULL || *keyname == NULL)
+			{
+				printfPQExpBuffer(&conn->errorMessage,
+								  libpq_gettext("out of memory\n"));
+				goto fail;
+			}
+
+			fnbuf[0] = '\0';	/* indicate we're not going to load from a
+								 * file */
+		}
+		else
+#endif   /* USE_SSL_ENGINE */
+		{
+			/* PGSSLKEY is not an engine, treat it as a filename */
+			strncpy(fnbuf, conn->sslkey, sizeof(fnbuf));
+		}
+	}
+	else if (have_homedir)
+	{
+		/* No PGSSLKEY specified, load default file */
+		snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_KEY_FILE);
+	}
+	else
+		fnbuf[0] = '\0';
+
+	if (*sslcert && fnbuf[0] != '\0')
+	{
+		/* read the client key from file */
+
+		if (stat(fnbuf, &buf) != 0)
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("certificate present, but not private key file \"%s\"\n"),
+							  fnbuf);
+			goto fail;
+		}
+#ifndef WIN32
+		if (!S_ISREG(buf.st_mode) || buf.st_mode & (S_IRWXG | S_IRWXO))
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("private key file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"),
+							  fnbuf);
+			goto fail;
+		}
+#endif
+
+		*sslkey = strdup(fnbuf);
+		if (*sslkey == NULL)
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("out of memory\n"));
+			goto fail;
+		}
+	}
+
+	/*
+	 * If the root cert file exists, load it so we can perform certificate
+	 * verification. If sslmode is "verify-full" we will also do further
+	 * verification after the connection has been completed.
+	 */
+	if (conn->sslrootcert && strlen(conn->sslrootcert) > 0)
+		strncpy(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' &&
+		stat(fnbuf, &buf) == 0)
+	{
+		*sslrootcert = strdup(fnbuf);
+		if (*sslrootcert == NULL)
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("out of memory\n"));
+			goto fail;
+		}
+	}
+	else
+	{
+		/*
+		 * 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);
+			goto fail;
+		}
+	}
+
+	/* If we have a root certificate, also try to load a CRL */
+	if (*sslrootcert)
+	{
+		if (conn->sslcrl && strlen(conn->sslcrl) > 0)
+			strncpy(fnbuf, conn->sslcrl, sizeof(fnbuf));
+		else if (have_homedir)
+			snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CRL_FILE);
+		else
+			fnbuf[0] = '\0';
+
+		if (fnbuf[0] != '\0' &&
+			stat(fnbuf, &buf) != 0)
+		{
+			/*
+			 * If not found, silently ignore;  we do not require CRL.
+			 * Any other error, however, is grounds for complaint.
+			 */
+			if (errno != ENOENT && errno != ENOTDIR)
+			{
+				printfPQExpBuffer(&conn->errorMessage,
+			   libpq_gettext("could not open certificate file \"%s\": %s\n"),
+							  fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
+				goto fail;
+			}
+		}
+		else
+		{
+			*sslcrl = strdup(fnbuf);
+			if (*sslcrl == NULL)
+			{
+				printfPQExpBuffer(&conn->errorMessage,
+								  libpq_gettext("out of memory\n"));
+				goto fail;
+			}
+		}
+	}
+
+	/* all done! */
+	return true;
+
+fail:
+	if (*sslcert)
+		free(*sslcert);
+	if (*engine)
+		free(*engine);
+	if (*keyname)
+		free(*keyname);
+	if (*sslkey)
+		free(*sslkey);
+	if (*sslrootcert)
+		free(*sslrootcert);
+	if (*sslcrl)
+		free(*sslcrl);
+
+	*sslcert = NULL;
+	*engine = NULL;
+	*keyname = NULL;
+	*sslkey = NULL;
+	*sslrootcert = NULL;
+	*sslcrl = NULL;
+
+	return false;
+}
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 6032904..35b1c9e 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -82,6 +82,10 @@ typedef struct
 #endif
 #endif   /* USE_OPENSSL */
 
+#ifdef USE_WINDOWS_SCHANNEL
+struct SchannelContext;
+#endif
+
 /*
  * POSTGRES backend dependent Constants.
  */
@@ -440,6 +444,10 @@ struct pg_conn
 #endif   /* USE_OPENSSL */
 #endif   /* USE_SSL */
 
+#ifdef USE_WINDOWS_SCHANNEL
+	struct SchannelContext *windows_schannel;
+#endif   /* USE_WINDOWS_SCHANNEL */
+
 #ifdef ENABLE_GSS
 	gss_ctx_id_t gctx;			/* GSS context */
 	gss_name_t	gtarg_nam;		/* GSS target name */
@@ -627,6 +635,8 @@ extern ssize_t pqsecure_write(PGconn *, const void *ptr, size_t len);
 extern ssize_t pqsecure_raw_read(PGconn *, void *ptr, size_t len);
 extern ssize_t pqsecure_raw_write(PGconn *, const void *ptr, size_t len);
 
+extern bool pqsecure_get_ssl_files(PGconn *, char **sslcert, char **sslkey, char **engine, char **keyname, char **sslrootcert, char **sslcrl);
+
 #if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
 extern int	pq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending);
 extern void pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending,
diff --git a/src/interfaces/libpq/win32.mak b/src/interfaces/libpq/win32.mak
index 39a0bc9..5793fce 100644
--- a/src/interfaces/libpq/win32.mak
+++ b/src/interfaces/libpq/win32.mak
@@ -127,6 +127,9 @@ CLEAN :
 !IFDEF USE_OPENSSL
 	-@erase "$(INTDIR)\fe-secure-openssl.obj"
 !ENDIF
+!IFDEF USE_WINDOWS_SCHANNEL
+	-@erase "$(INTDIR)\fe-secure-winschannel.obj"
+!ENDIF
 
 
 LIB32=link.exe -lib
@@ -170,7 +173,9 @@ LIB32_OBJS= \
 !IFDEF USE_OPENSSL
 	LIB32_OBJS=$(LIB32_OBJS) "$(INTDIR)\fe-secure-openssl.obj"
 !ENDIF
-
+!IFDEF USE_WINDOWS_SCHANNEL
+	LIB32_OBJS=$(LIB32_OBJS) "$(INTDIR)\fe-secure-winschannel.obj"
+!ENDIF
 
 config: ..\..\include\pg_config.h ..\..\include\pg_config_ext.h pg_config_paths.h  ..\..\include\pg_config_os.h
 
@@ -200,6 +205,11 @@ CPP_PROJ=$(CPP_PROJ) /D USE_OPENSSL
 SSL_LIBS=ssleay32.lib libeay32.lib gdi32.lib
 !ENDIF
 
+!IFDEF USE_WINDOWS_SCHANNEL
+CPP_PROJ=$(CPP_PROJ) /D USE_WINDOWS_SCHANNEL
+SSL_LIBS=crypt32.lib
+!ENDIF
+
 !IFDEF USE_KFW
 CPP_PROJ=$(CPP_PROJ) /D KRB5
 KFW_LIBS=krb5_32.lib comerr32.lib gssapi32.lib
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index e6fb3ec..fa2ac8c 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -115,6 +115,10 @@ sub mkvcbuild
 	$postgres->AddDefine('BUILDING_DLL');
 	$postgres->AddLibrary('secur32.lib');
 	$postgres->AddLibrary('ws2_32.lib');
+	if ($solution->{options}->{winschannel})
+	{
+		$postgres->AddLibrary('crypt32.lib');
+	}
 	$postgres->AddLibrary('wldap32.lib') if ($solution->{options}->{ldap});
 	$postgres->FullExportDLL('postgres.lib');
 	# The OBJS scraper doesn't know about ifdefs, so remove be-secure-openssl.c
@@ -123,6 +127,11 @@ sub mkvcbuild
 	{
 		$postgres->RemoveFile('src\backend\libpq\be-secure-openssl.c');
 	}
+	# and same for winschannel
+	if (!$solution->{options}->{winschannel})
+	{
+		$postgres->RemoveFile('src\backend\libpq\be-secure-winschannel.c');
+	}
 
 	my $snowball = $solution->AddProject('dict_snowball', 'dll', '',
 		'src\backend\snowball');
@@ -276,6 +285,10 @@ sub mkvcbuild
 	$libpq->AddDefine('UNSAFE_STAT_OK');
 	$libpq->AddIncludeDir('src\port');
 	$libpq->AddLibrary('secur32.lib');
+	if ($solution->{options}->{winschannel})
+	{
+		$libpq->AddLibrary('crypt32.lib');
+	}
 	$libpq->AddLibrary('ws2_32.lib');
 	$libpq->AddLibrary('wldap32.lib') if ($solution->{options}->{ldap});
 	$libpq->UseDef('src\interfaces\libpq\libpqdll.def');
@@ -288,6 +301,11 @@ sub mkvcbuild
 	{
 		$libpq->RemoveFile('src\interfaces\libpq\fe-secure-openssl.c');
 	}
+	# and same for winschannel
+	if (!$solution->{options}->{winschannel})
+	{
+		$libpq->RemoveFile('src\interfaces\libpq\fe-secure-winschannel.c');
+	}
 
 	my $libpqwalreceiver =
 	  $solution->AddProject('libpqwalreceiver', 'dll', '',
diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm
index 39e41f6..7034eea 100644
--- a/src/tools/msvc/Solution.pm
+++ b/src/tools/msvc/Solution.pm
@@ -183,6 +183,8 @@ sub GenerateFiles
 		print O "#define USE_LDAP 1\n"   if ($self->{options}->{ldap});
 		print O "#define HAVE_LIBZ 1\n"  if ($self->{options}->{zlib});
 		print O "#define USE_OPENSSL 1\n" if ($self->{options}->{openssl});
+		print O "#define USE_WINDOWS_SCHANNEL 1\n"
+		  if ($self->{options}->{winschannel});
 		print O "#define ENABLE_NLS 1\n" if ($self->{options}->{nls});
 
 		print O "#define BLCKSZ ", 1024 * $self->{options}->{blocksize}, "\n";
@@ -629,6 +631,7 @@ sub GetFakeConfigure
 	$cfg .= ' --without-zlib' unless ($self->{options}->{zlib});
 	$cfg .= ' --with-extra-version' if ($self->{options}->{extraver});
 	$cfg .= ' --with-openssl'       if ($self->{options}->{openssl});
+	$cfg .= ' --with-winschannel'   if ($self->{options}->{winschannel});
 	$cfg .= ' --with-ossp-uuid'     if ($self->{options}->{uuid});
 	$cfg .= ' --with-libxml'        if ($self->{options}->{xml});
 	$cfg .= ' --with-libxslt'       if ($self->{options}->{xslt});
diff --git a/src/tools/msvc/config_default.pl b/src/tools/msvc/config_default.pl
index e4d4810..ab4001a 100644
--- a/src/tools/msvc/config_default.pl
+++ b/src/tools/msvc/config_default.pl
@@ -17,6 +17,7 @@ our $config = {
 	perl     => undef,   # --with-perl
 	python   => undef,   # --with-python=<path>
 	openssl  => undef,   # --with-openssl=<path>
+	winschannel => 1,    # --with-winschannel
 	uuid     => undef,   # --with-ossp-uuid
 	xml      => undef,   # --with-libxml=<path>
 	xslt     => undef,   # --with-libxslt=<path>
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to