Hello friends,

For your consideration, here is a new version of GSSAPI encryption
support.  For those who prefer, it's also available on my github:
https://github.com/frozencemetery/postgres/commit/c92275b6605d7929cda5551de47a4c60aab7179e

Some thoughts:

- The overall design is different this time - GSS encryption sits in
  parallel construction to SSL encryption rather than at the protocol
  level - so a strict diff probably isn't useful.

- The GSSAPI authentication code has been moved without modification.
  In doing so, the temptation to modify it (flags, error checking, that
  big comment at the top about things from Athena, etc.)  is very large.
  I do not know whether these changes are best suited to another patch
  in this series or should be reviewed separately.  I am also hesitant
  to add things beyond the core before I am told this is the right
  approach.

- There's no fallback here.  I wrote fallback support for versions 1-3,
  and the same design could apply here without too much trouble, but I
  am hesitant to port it over before the encryption design is approved.
  I strongly suspect you will not want to merge this without fallback
  support, and that makes sense to me.

- The client and server code look a lot like each other.  This
  resemblance is not exact, and my understanding is that server and
  client need to compile independently, so I do not know of a way to
  rectify this.  Suggestions are welcome.

Thanks!
From c92275b6605d7929cda5551de47a4c60aab7179e Mon Sep 17 00:00:00 2001
From: Robbie Harwood <rharw...@redhat.com>
Date: Tue, 17 Nov 2015 18:34:14 -0500
Subject: [PATCH] Connect encryption support for GSSAPI

Existing GSSAPI authentication code is extended to support connection
encryption.  Connection begins as soon as possible - that is,
immediately after the client and server complete authentication.
---
 configure                           |   2 +
 configure.in                        |   1 +
 doc/src/sgml/client-auth.sgml       |   2 +-
 doc/src/sgml/runtime.sgml           |  20 +-
 src/Makefile.global.in              |   1 +
 src/backend/libpq/Makefile          |   4 +
 src/backend/libpq/auth.c            | 330 +-------------------
 src/backend/libpq/be-gssapi.c       | 584 ++++++++++++++++++++++++++++++++++++
 src/backend/libpq/be-secure.c       |  16 +
 src/backend/postmaster/postmaster.c |  12 +
 src/include/libpq/libpq-be.h        |  31 ++
 src/interfaces/libpq/Makefile       |   4 +
 src/interfaces/libpq/fe-auth.c      | 182 -----------
 src/interfaces/libpq/fe-auth.h      |   5 +
 src/interfaces/libpq/fe-connect.c   |  10 +
 src/interfaces/libpq/fe-gssapi.c    | 475 +++++++++++++++++++++++++++++
 src/interfaces/libpq/fe-secure.c    |  16 +-
 src/interfaces/libpq/libpq-int.h    |  16 +-
 18 files changed, 1193 insertions(+), 518 deletions(-)
 create mode 100644 src/backend/libpq/be-gssapi.c
 create mode 100644 src/interfaces/libpq/fe-gssapi.c

diff --git a/configure b/configure
index 3dd1b15..7fd7610 100755
--- a/configure
+++ b/configure
@@ -712,6 +712,7 @@ with_uuid
 with_selinux
 with_openssl
 krb_srvtab
+with_gssapi
 with_python
 with_perl
 with_tcl
@@ -5488,6 +5489,7 @@ $as_echo "$with_gssapi" >&6; }
 
 
 
+
 #
 # Kerberos configuration parameters
 #
diff --git a/configure.in b/configure.in
index 9398482..b19932e 100644
--- a/configure.in
+++ b/configure.in
@@ -636,6 +636,7 @@ PGAC_ARG_BOOL(with, gssapi, no, [build with GSSAPI support],
   krb_srvtab="FILE:\$(sysconfdir)/krb5.keytab"
 ])
 AC_MSG_RESULT([$with_gssapi])
+AC_SUBST(with_gssapi)
 
 
 AC_SUBST(krb_srvtab)
diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml
index 3b2935c..7d37223 100644
--- a/doc/src/sgml/client-auth.sgml
+++ b/doc/src/sgml/client-auth.sgml
@@ -915,7 +915,7 @@ omicron         bryanh                  guest1
     provides automatic authentication (single sign-on) for systems
     that support it. The authentication itself is secure, but the
     data sent over the database connection will be sent unencrypted unless
-    <acronym>SSL</acronym> is used.
+    <acronym>SSL</acronym> or <acronym>GSSAPI</acronym> are used.
    </para>
 
    <para>
diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml
index cda05f5..bd8156f 100644
--- a/doc/src/sgml/runtime.sgml
+++ b/doc/src/sgml/runtime.sgml
@@ -1880,12 +1880,13 @@ pg_dumpall -p 5432 | psql -d postgres -p 5433
   </para>
 
   <para>
-   To prevent spoofing on TCP connections, the best solution is to use
-   SSL certificates and make sure that clients check the server's certificate.
-   To do that, the server
-   must be configured to accept only <literal>hostssl</> connections (<xref
-   linkend="auth-pg-hba-conf">) and have SSL key and certificate files
-   (<xref linkend="ssl-tcp">). The TCP client must connect using
+   To prevent spoofing on TCP connections, the best solutions are either to
+   use GSSAPI for authentication and encryption or to use SSL certificates and
+   make sure that clients check the server's certificate.  To secure using
+   SSL, the server must be configured to accept only <literal>hostssl</>
+   connections (<xref linkend="auth-pg-hba-conf">) and have SSL key and
+   certificate files (<xref linkend="ssl-tcp">). The TCP client must connect
+   using
    <literal>sslmode=verify-ca</> or
    <literal>verify-full</> and have the appropriate root certificate
    file installed (<xref linkend="libq-ssl-certificates">).
@@ -2005,6 +2006,13 @@ pg_dumpall -p 5432 | psql -d postgres -p 5433
       connect to servers only via SSL. <application>Stunnel</> or
       <application>SSH</> can also be used to encrypt transmissions.
      </para>
+
+     <para>
+       Similarly, GSSAPI also encrypts all data sent across the network,
+       including passwords, queries, and data, as in
+       SSL. <filename>pg_hba.conf</> allows specification of GSSAPI
+       connections, which are always encrypted.
+     </para>
     </listitem>
   </varlistentry>
 
diff --git a/src/Makefile.global.in b/src/Makefile.global.in
index 51f4797..842a397 100644
--- a/src/Makefile.global.in
+++ b/src/Makefile.global.in
@@ -183,6 +183,7 @@ with_perl	= @with_perl@
 with_python	= @with_python@
 with_tcl	= @with_tcl@
 with_openssl	= @with_openssl@
+with_gssapi	= @with_gssapi@
 with_selinux	= @with_selinux@
 with_libxml	= @with_libxml@
 with_libxslt	= @with_libxslt@
diff --git a/src/backend/libpq/Makefile b/src/backend/libpq/Makefile
index 09410c4..b80ae74 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_gssapi),yes)
+OBJS += be-gssapi.o
+endif
+
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 57c2f48..c0366fd 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -22,6 +22,7 @@
 #include <unistd.h>
 
 #include "libpq/auth.h"
+#include "libpq/libpq-be.h"
 #include "libpq/crypt.h"
 #include "libpq/ip.h"
 #include "libpq/libpq.h"
@@ -36,7 +37,7 @@
  * Global authentication functions
  *----------------------------------------------------------------
  */
-static void sendAuthRequest(Port *port, AuthRequest areq);
+void sendAuthRequest(Port *port, AuthRequest areq);
 static void auth_failed(Port *port, int status, char *logdetail);
 static char *recv_password_packet(Port *port);
 static int	recv_and_check_password_packet(Port *port, char **logdetail);
@@ -132,21 +133,6 @@ bool		pg_krb_caseins_users;
 
 
 /*----------------------------------------------------------------
- * GSSAPI Authentication
- *----------------------------------------------------------------
- */
-#ifdef ENABLE_GSS
-#if defined(HAVE_GSSAPI_H)
-#include <gssapi.h>
-#else
-#include <gssapi/gssapi.h>
-#endif
-
-static int	pg_GSS_recvauth(Port *port);
-#endif   /* ENABLE_GSS */
-
-
-/*----------------------------------------------------------------
  * SSPI Authentication
  *----------------------------------------------------------------
  */
@@ -167,22 +153,6 @@ static int	pg_SSPI_recvauth(Port *port);
 static int	CheckRADIUSAuth(Port *port);
 
 
-/*
- * Maximum accepted size of GSS and SSPI authentication tokens.
- *
- * Kerberos tickets are usually quite small, but the TGTs issued by Windows
- * domain controllers include an authorization field known as the Privilege
- * Attribute Certificate (PAC), which contains the user's Windows permissions
- * (group memberships etc.). The PAC is copied into all tickets obtained on
- * the basis of this TGT (even those issued by Unix realms which the Windows
- * realm trusts), and can be several kB in size. The maximum token size
- * accepted by Windows systems is determined by the MaxAuthToken Windows
- * registry setting. Microsoft recommends that it is not set higher than
- * 65535 bytes, so that seems like a reasonable limit for us as well.
- */
-#define PG_MAX_AUTH_TOKEN_LENGTH	65535
-
-
 /*----------------------------------------------------------------
  * Global authentication functions
  *----------------------------------------------------------------
@@ -565,7 +535,7 @@ ClientAuthentication(Port *port)
 /*
  * Send an authentication request packet to the frontend.
  */
-static void
+void
 sendAuthRequest(Port *port, AuthRequest areq)
 {
 	StringInfoData buf;
@@ -707,300 +677,6 @@ recv_and_check_password_packet(Port *port, char **logdetail)
 	return result;
 }
 
-
-
-/*----------------------------------------------------------------
- * GSSAPI authentication system
- *----------------------------------------------------------------
- */
-#ifdef ENABLE_GSS
-
-#if defined(WIN32) && !defined(WIN32_ONLY_COMPILER)
-/*
- * MIT Kerberos GSSAPI DLL doesn't properly export the symbols for MingW
- * that contain the OIDs required. Redefine here, values copied
- * from src/athena/auth/krb5/src/lib/gssapi/generic/gssapi_generic.c
- */
-static const gss_OID_desc GSS_C_NT_USER_NAME_desc =
-{10, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x02"};
-static GSS_DLLIMP gss_OID GSS_C_NT_USER_NAME = &GSS_C_NT_USER_NAME_desc;
-#endif
-
-
-static void
-pg_GSS_error(int severity, char *errmsg, OM_uint32 maj_stat, OM_uint32 min_stat)
-{
-	gss_buffer_desc gmsg;
-	OM_uint32	lmin_s,
-				msg_ctx;
-	char		msg_major[128],
-				msg_minor[128];
-
-	/* Fetch major status message */
-	msg_ctx = 0;
-	gss_display_status(&lmin_s, maj_stat, GSS_C_GSS_CODE,
-					   GSS_C_NO_OID, &msg_ctx, &gmsg);
-	strlcpy(msg_major, gmsg.value, sizeof(msg_major));
-	gss_release_buffer(&lmin_s, &gmsg);
-
-	if (msg_ctx)
-
-		/*
-		 * More than one message available. XXX: Should we loop and read all
-		 * messages? (same below)
-		 */
-		ereport(WARNING,
-				(errmsg_internal("incomplete GSS error report")));
-
-	/* Fetch mechanism minor status message */
-	msg_ctx = 0;
-	gss_display_status(&lmin_s, min_stat, GSS_C_MECH_CODE,
-					   GSS_C_NO_OID, &msg_ctx, &gmsg);
-	strlcpy(msg_minor, gmsg.value, sizeof(msg_minor));
-	gss_release_buffer(&lmin_s, &gmsg);
-
-	if (msg_ctx)
-		ereport(WARNING,
-				(errmsg_internal("incomplete GSS minor error report")));
-
-	/*
-	 * errmsg_internal, since translation of the first part must be done
-	 * before calling this function anyway.
-	 */
-	ereport(severity,
-			(errmsg_internal("%s", errmsg),
-			 errdetail_internal("%s: %s", msg_major, msg_minor)));
-}
-
-static int
-pg_GSS_recvauth(Port *port)
-{
-	OM_uint32	maj_stat,
-				min_stat,
-				lmin_s,
-				gflags;
-	int			mtype;
-	int			ret;
-	StringInfoData buf;
-	gss_buffer_desc gbuf;
-
-	/*
-	 * GSS auth is not supported for protocol versions before 3, because it
-	 * relies on the overall message length word to determine the GSS payload
-	 * size in AuthenticationGSSContinue and PasswordMessage messages. (This
-	 * is, in fact, a design error in our GSS support, because protocol
-	 * messages are supposed to be parsable without relying on the length
-	 * word; but it's not worth changing it now.)
-	 */
-	if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
-		ereport(FATAL,
-				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("GSSAPI is not supported in protocol version 2")));
-
-	if (pg_krb_server_keyfile && strlen(pg_krb_server_keyfile) > 0)
-	{
-		/*
-		 * Set default Kerberos keytab file for the Krb5 mechanism.
-		 *
-		 * setenv("KRB5_KTNAME", pg_krb_server_keyfile, 0); except setenv()
-		 * not always available.
-		 */
-		if (getenv("KRB5_KTNAME") == NULL)
-		{
-			size_t		kt_len = strlen(pg_krb_server_keyfile) + 14;
-			char	   *kt_path = malloc(kt_len);
-
-			if (!kt_path ||
-				snprintf(kt_path, kt_len, "KRB5_KTNAME=%s",
-						 pg_krb_server_keyfile) != kt_len - 2 ||
-				putenv(kt_path) != 0)
-			{
-				ereport(LOG,
-						(errcode(ERRCODE_OUT_OF_MEMORY),
-						 errmsg("out of memory")));
-				return STATUS_ERROR;
-			}
-		}
-	}
-
-	/*
-	 * We accept any service principal that's present in our keytab. This
-	 * increases interoperability between kerberos implementations that see
-	 * for example case sensitivity differently, while not really opening up
-	 * any vector of attack.
-	 */
-	port->gss->cred = GSS_C_NO_CREDENTIAL;
-
-	/*
-	 * Initialize sequence with an empty context
-	 */
-	port->gss->ctx = GSS_C_NO_CONTEXT;
-
-	/*
-	 * Loop through GSSAPI message exchange. This exchange can consist of
-	 * multiple messags sent in both directions. First message is always from
-	 * the client. All messages from client to server are password packets
-	 * (type 'p').
-	 */
-	do
-	{
-		pq_startmsgread();
-
-		CHECK_FOR_INTERRUPTS();
-
-		mtype = pq_getbyte();
-		if (mtype != 'p')
-		{
-			/* Only log error if client didn't disconnect. */
-			if (mtype != EOF)
-				ereport(COMMERROR,
-						(errcode(ERRCODE_PROTOCOL_VIOLATION),
-						 errmsg("expected GSS response, got message type %d",
-								mtype)));
-			return STATUS_ERROR;
-		}
-
-		/* Get the actual GSS token */
-		initStringInfo(&buf);
-		if (pq_getmessage(&buf, PG_MAX_AUTH_TOKEN_LENGTH))
-		{
-			/* EOF - pq_getmessage already logged error */
-			pfree(buf.data);
-			return STATUS_ERROR;
-		}
-
-		/* Map to GSSAPI style buffer */
-		gbuf.length = buf.len;
-		gbuf.value = buf.data;
-
-		elog(DEBUG4, "Processing received GSS token of length %u",
-			 (unsigned int) gbuf.length);
-
-		maj_stat = gss_accept_sec_context(
-										  &min_stat,
-										  &port->gss->ctx,
-										  port->gss->cred,
-										  &gbuf,
-										  GSS_C_NO_CHANNEL_BINDINGS,
-										  &port->gss->name,
-										  NULL,
-										  &port->gss->outbuf,
-										  &gflags,
-										  NULL,
-										  NULL);
-
-		/* gbuf no longer used */
-		pfree(buf.data);
-
-		elog(DEBUG5, "gss_accept_sec_context major: %d, "
-			 "minor: %d, outlen: %u, outflags: %x",
-			 maj_stat, min_stat,
-			 (unsigned int) port->gss->outbuf.length, gflags);
-
-		CHECK_FOR_INTERRUPTS();
-
-		if (port->gss->outbuf.length != 0)
-		{
-			/*
-			 * Negotiation generated data to be sent to the client.
-			 */
-			elog(DEBUG4, "sending GSS response token of length %u",
-				 (unsigned int) port->gss->outbuf.length);
-
-			sendAuthRequest(port, AUTH_REQ_GSS_CONT);
-
-			gss_release_buffer(&lmin_s, &port->gss->outbuf);
-		}
-
-		if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
-		{
-			gss_delete_sec_context(&lmin_s, &port->gss->ctx, GSS_C_NO_BUFFER);
-			pg_GSS_error(ERROR,
-					   gettext_noop("accepting GSS security context failed"),
-						 maj_stat, min_stat);
-		}
-
-		if (maj_stat == GSS_S_CONTINUE_NEEDED)
-			elog(DEBUG4, "GSS continue needed");
-
-	} while (maj_stat == GSS_S_CONTINUE_NEEDED);
-
-	if (port->gss->cred != GSS_C_NO_CREDENTIAL)
-	{
-		/*
-		 * Release service principal credentials
-		 */
-		gss_release_cred(&min_stat, &port->gss->cred);
-	}
-
-	/*
-	 * GSS_S_COMPLETE indicates that authentication is now complete.
-	 *
-	 * Get the name of the user that authenticated, and compare it to the pg
-	 * username that was specified for the connection.
-	 */
-	maj_stat = gss_display_name(&min_stat, port->gss->name, &gbuf, NULL);
-	if (maj_stat != GSS_S_COMPLETE)
-		pg_GSS_error(ERROR,
-					 gettext_noop("retrieving GSS user name failed"),
-					 maj_stat, min_stat);
-
-	/*
-	 * Split the username at the realm separator
-	 */
-	if (strchr(gbuf.value, '@'))
-	{
-		char	   *cp = strchr(gbuf.value, '@');
-
-		/*
-		 * If we are not going to include the realm in the username that is
-		 * passed to the ident map, destructively modify it here to remove the
-		 * realm. Then advance past the separator to check the realm.
-		 */
-		if (!port->hba->include_realm)
-			*cp = '\0';
-		cp++;
-
-		if (port->hba->krb_realm != NULL && strlen(port->hba->krb_realm))
-		{
-			/*
-			 * Match the realm part of the name first
-			 */
-			if (pg_krb_caseins_users)
-				ret = pg_strcasecmp(port->hba->krb_realm, cp);
-			else
-				ret = strcmp(port->hba->krb_realm, cp);
-
-			if (ret)
-			{
-				/* GSS realm does not match */
-				elog(DEBUG2,
-				   "GSSAPI realm (%s) and configured realm (%s) don't match",
-					 cp, port->hba->krb_realm);
-				gss_release_buffer(&lmin_s, &gbuf);
-				return STATUS_ERROR;
-			}
-		}
-	}
-	else if (port->hba->krb_realm && strlen(port->hba->krb_realm))
-	{
-		elog(DEBUG2,
-			 "GSSAPI did not return realm but realm matching was requested");
-
-		gss_release_buffer(&lmin_s, &gbuf);
-		return STATUS_ERROR;
-	}
-
-	ret = check_usermap(port->hba->usermap, port->user_name, gbuf.value,
-						pg_krb_caseins_users);
-
-	gss_release_buffer(&lmin_s, &gbuf);
-
-	return ret;
-}
-#endif   /* ENABLE_GSS */
-
-
 /*----------------------------------------------------------------
  * SSPI authentication system
  *----------------------------------------------------------------
diff --git a/src/backend/libpq/be-gssapi.c b/src/backend/libpq/be-gssapi.c
new file mode 100644
index 0000000..04d8a98
--- /dev/null
+++ b/src/backend/libpq/be-gssapi.c
@@ -0,0 +1,584 @@
+/*-------------------------------------------------------------------------
+ *
+ * be-gssapi.c
+ *	  GSSAPI authentication and encryption support
+ *
+ * Portions Copyright (c) 2015-2016, Red Hat, Inc.
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/libpq/auth.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "libpq/libpq.h"
+#include "libpq/libpq-be.h"
+#include "libpq/pqformat.h"
+#include "miscadmin.h"
+
+#if defined(WIN32) && !defined(WIN32_ONLY_COMPILER)
+/*
+ * MIT Kerberos GSSAPI DLL doesn't properly export the symbols for MingW
+ * that contain the OIDs required. Redefine here, values copied
+ * from src/athena/auth/krb5/src/lib/gssapi/generic/gssapi_generic.c
+ */
+static const gss_OID_desc GSS_C_NT_USER_NAME_desc =
+{10, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x02"};
+static GSS_DLLIMP gss_OID GSS_C_NT_USER_NAME = &GSS_C_NT_USER_NAME_desc;
+#endif
+
+static void
+pg_GSS_error(int severity, char *errmsg, OM_uint32 maj_stat, OM_uint32 min_stat)
+{
+	gss_buffer_desc gmsg;
+	OM_uint32	lmin_s,
+				msg_ctx;
+	char		msg_major[128],
+				msg_minor[128];
+
+	/* Fetch major status message */
+	msg_ctx = 0;
+	gss_display_status(&lmin_s, maj_stat, GSS_C_GSS_CODE,
+					   GSS_C_NO_OID, &msg_ctx, &gmsg);
+	strlcpy(msg_major, gmsg.value, sizeof(msg_major));
+	gss_release_buffer(&lmin_s, &gmsg);
+
+	if (msg_ctx)
+
+		/*
+		 * More than one message available. XXX: Should we loop and read all
+		 * messages? (same below)
+		 */
+		ereport(WARNING,
+				(errmsg_internal("incomplete GSS error report")));
+
+	/* Fetch mechanism minor status message */
+	msg_ctx = 0;
+	gss_display_status(&lmin_s, min_stat, GSS_C_MECH_CODE,
+					   GSS_C_NO_OID, &msg_ctx, &gmsg);
+	strlcpy(msg_minor, gmsg.value, sizeof(msg_minor));
+	gss_release_buffer(&lmin_s, &gmsg);
+
+	if (msg_ctx)
+		ereport(WARNING,
+				(errmsg_internal("incomplete GSS minor error report")));
+
+	/*
+	 * errmsg_internal, since translation of the first part must be done
+	 * before calling this function anyway.
+	 */
+	ereport(severity,
+			(errmsg_internal("%s", errmsg),
+			 errdetail_internal("%s: %s", msg_major, msg_minor)));
+}
+
+int
+pg_GSS_recvauth(Port *port)
+{
+	OM_uint32	maj_stat,
+				min_stat,
+				lmin_s,
+				gflags;
+	int			mtype;
+	int			ret;
+	StringInfoData buf;
+	gss_buffer_desc gbuf;
+
+	/*
+	 * GSS auth is not supported for protocol versions before 3, because it
+	 * relies on the overall message length word to determine the GSS payload
+	 * size in AuthenticationGSSContinue and PasswordMessage messages. (This
+	 * is, in fact, a design error in our GSS support, because protocol
+	 * messages are supposed to be parsable without relying on the length
+	 * word; but it's not worth changing it now.)
+	 */
+	if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
+		ereport(FATAL,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("GSSAPI is not supported in protocol version 2")));
+
+	if (pg_krb_server_keyfile && strlen(pg_krb_server_keyfile) > 0)
+	{
+		/*
+		 * Set default Kerberos keytab file for the Krb5 mechanism.
+		 *
+		 * setenv("KRB5_KTNAME", pg_krb_server_keyfile, 0); except setenv()
+		 * not always available.
+		 */
+		if (getenv("KRB5_KTNAME") == NULL)
+		{
+			size_t		kt_len = strlen(pg_krb_server_keyfile) + 14;
+			char	   *kt_path = malloc(kt_len);
+
+			if (!kt_path ||
+				snprintf(kt_path, kt_len, "KRB5_KTNAME=%s",
+						 pg_krb_server_keyfile) != kt_len - 2 ||
+				putenv(kt_path) != 0)
+			{
+				ereport(LOG,
+						(errcode(ERRCODE_OUT_OF_MEMORY),
+						 errmsg("out of memory")));
+				return STATUS_ERROR;
+			}
+		}
+	}
+
+	/*
+	 * We accept any service principal that's present in our keytab. This
+	 * increases interoperability between kerberos implementations that see
+	 * for example case sensitivity differently, while not really opening up
+	 * any vector of attack.
+	 */
+	port->gss->cred = GSS_C_NO_CREDENTIAL;
+
+	/*
+	 * Initialize sequence with an empty context
+	 */
+	port->gss->ctx = GSS_C_NO_CONTEXT;
+
+	/*
+	 * Loop through GSSAPI message exchange. This exchange can consist of
+	 * multiple messags sent in both directions. First message is always from
+	 * the client. All messages from client to server are password packets
+	 * (type 'p').
+	 */
+	do
+	{
+		pq_startmsgread();
+
+		CHECK_FOR_INTERRUPTS();
+
+		mtype = pq_getbyte();
+		if (mtype != 'p')
+		{
+			/* Only log error if client didn't disconnect. */
+			if (mtype != EOF)
+				ereport(COMMERROR,
+						(errcode(ERRCODE_PROTOCOL_VIOLATION),
+						 errmsg("expected GSS response, got message type %d",
+								mtype)));
+			return STATUS_ERROR;
+		}
+
+		/* Get the actual GSS token */
+		initStringInfo(&buf);
+		if (pq_getmessage(&buf, PG_MAX_AUTH_TOKEN_LENGTH))
+		{
+			/* EOF - pq_getmessage already logged error */
+			pfree(buf.data);
+			return STATUS_ERROR;
+		}
+
+		/* Map to GSSAPI style buffer */
+		gbuf.length = buf.len;
+		gbuf.value = buf.data;
+
+		elog(DEBUG4, "Processing received GSS token of length %u",
+			 (unsigned int) gbuf.length);
+
+		maj_stat = gss_accept_sec_context(
+										  &min_stat,
+										  &port->gss->ctx,
+										  port->gss->cred,
+										  &gbuf,
+										  GSS_C_NO_CHANNEL_BINDINGS,
+										  &port->gss->name,
+										  NULL,
+										  &port->gss->outbuf,
+										  &gflags,
+										  NULL,
+										  NULL);
+
+		/* gbuf no longer used */
+		pfree(buf.data);
+
+		elog(DEBUG5, "gss_accept_sec_context major: %d, "
+			 "minor: %d, outlen: %u, outflags: %x",
+			 maj_stat, min_stat,
+			 (unsigned int) port->gss->outbuf.length, gflags);
+
+		CHECK_FOR_INTERRUPTS();
+
+		if (port->gss->outbuf.length != 0)
+		{
+			/*
+			 * Negotiation generated data to be sent to the client.
+			 */
+			elog(DEBUG4, "sending GSS response token of length %u",
+				 (unsigned int) port->gss->outbuf.length);
+
+			sendAuthRequest(port, AUTH_REQ_GSS_CONT);
+
+			gss_release_buffer(&lmin_s, &port->gss->outbuf);
+		}
+
+		if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
+		{
+			gss_delete_sec_context(&lmin_s, &port->gss->ctx, GSS_C_NO_BUFFER);
+			pg_GSS_error(ERROR,
+					   gettext_noop("accepting GSS security context failed"),
+						 maj_stat, min_stat);
+		}
+
+		if (maj_stat == GSS_S_CONTINUE_NEEDED)
+			elog(DEBUG4, "GSS continue needed");
+
+	} while (maj_stat == GSS_S_CONTINUE_NEEDED);
+
+	if (port->gss->cred != GSS_C_NO_CREDENTIAL)
+	{
+		/*
+		 * Release service principal credentials
+		 */
+		gss_release_cred(&min_stat, &port->gss->cred);
+	}
+
+	/*
+	 * GSS_S_COMPLETE indicates that authentication is now complete.
+	 *
+	 * Get the name of the user that authenticated, and compare it to the pg
+	 * username that was specified for the connection.
+	 */
+	maj_stat = gss_display_name(&min_stat, port->gss->name, &gbuf, NULL);
+	if (maj_stat != GSS_S_COMPLETE)
+		pg_GSS_error(ERROR,
+					 gettext_noop("retrieving GSS user name failed"),
+					 maj_stat, min_stat);
+
+	/*
+	 * Split the username at the realm separator
+	 */
+	if (strchr(gbuf.value, '@'))
+	{
+		char	   *cp = strchr(gbuf.value, '@');
+
+		/*
+		 * If we are not going to include the realm in the username that is
+		 * passed to the ident map, destructively modify it here to remove the
+		 * realm. Then advance past the separator to check the realm.
+		 */
+		if (!port->hba->include_realm)
+			*cp = '\0';
+		cp++;
+
+		if (port->hba->krb_realm != NULL && strlen(port->hba->krb_realm))
+		{
+			/*
+			 * Match the realm part of the name first
+			 */
+			if (pg_krb_caseins_users)
+				ret = pg_strcasecmp(port->hba->krb_realm, cp);
+			else
+				ret = strcmp(port->hba->krb_realm, cp);
+
+			if (ret)
+			{
+				/* GSS realm does not match */
+				elog(DEBUG2,
+				   "GSSAPI realm (%s) and configured realm (%s) don't match",
+					 cp, port->hba->krb_realm);
+				gss_release_buffer(&lmin_s, &gbuf);
+				return STATUS_ERROR;
+			}
+		}
+	}
+	else if (port->hba->krb_realm && strlen(port->hba->krb_realm))
+	{
+		elog(DEBUG2,
+			 "GSSAPI did not return realm but realm matching was requested");
+
+		gss_release_buffer(&lmin_s, &gbuf);
+		return STATUS_ERROR;
+	}
+
+	ret = check_usermap(port->hba->usermap, port->user_name, gbuf.value,
+						pg_krb_caseins_users);
+
+	gss_release_buffer(&lmin_s, &gbuf);
+
+	return ret;
+}
+
+static ssize_t
+be_gssapi_should_crypto(Port *port)
+{
+	OM_uint32 major, minor;
+	int open = 1;
+
+	if (port->gss->ctx == GSS_C_NO_CONTEXT)
+		return 0;
+	else if (port->gss->should_encrypt)
+		return 1;
+
+	major = gss_inquire_context(&minor, port->gss->ctx,
+								NULL, NULL, NULL, NULL, NULL, NULL,
+								&open);
+	if (major == GSS_S_NO_CONTEXT)
+	{
+		/*
+		 * In MIT krb5 < 1.14, it was not possible to call gss_inquire_context
+		 * on an incomplete context.  This was a violation of rfc2744 and has
+		 * been corrected in https://github.com/krb5/krb5/pull/285
+		 */
+		return 0;
+	}
+	else if (GSS_ERROR(major))
+	{
+		pg_GSS_error(ERROR,
+					 gettext_noop("GSSAPI context state error"),
+					 major, minor);
+		return -1;
+	}
+	else if (open != 0)
+	{
+		/*
+		 * Though we can start encrypting here, our client is not ready since
+		 * it has not received the final auth packet.  Set encryption on for
+		 * the next packet, but send this one in the clear.
+		 */
+		port->gss->should_encrypt = true;
+	}
+	return 0;
+}
+
+ssize_t
+be_gssapi_write(Port *port, void *ptr, size_t len)
+{
+	OM_uint32 major, minor;
+	gss_buffer_desc input, output;
+	ssize_t ret;
+	int conf;
+	uint32 netlen;
+	char lenbuf[4];
+	struct iovec iov[2];
+
+	ret = be_gssapi_should_crypto(port);
+	if (ret == -1)
+		return -1;
+	else if (ret == 0)
+		return secure_raw_write(port, ptr, len);
+
+	if (port->gss->writebuf.len != 0)
+	{
+		ret = send(port->sock,
+				   port->gss->writebuf.data + port->gss->writebuf.cursor,
+				   port->gss->writebuf.len - port->gss->writebuf.cursor,
+				   0);
+		if (ret < 0)
+			return ret;
+
+		port->gss->writebuf.cursor += ret;
+		if (port->gss->writebuf.cursor == port->gss->writebuf.len)
+		{
+			port->gss->writebuf.len = port->gss->writebuf.cursor = 0;
+			port->gss->writebuf.data[0] = '\0';
+			/* The entire request has now been written */
+			return len;
+		}
+		/* need to be called again */
+		return 0;
+	}
+
+	output.value = NULL;
+	output.length = 0;
+
+	input.value = ptr;
+	input.length = len;
+
+	conf = 0;
+	major = gss_wrap(&minor, port->gss->ctx, 1, GSS_C_QOP_DEFAULT,
+					 &input, &conf, &output);
+	if (GSS_ERROR(major))
+	{
+		pg_GSS_error(ERROR,
+					 gettext_noop("GSSAPI wrap error"),
+					 major, minor);
+		ret = -1;
+		goto cleanup;
+	}
+	else if (conf == 0)
+	{
+		ereport(FATAL, (errmsg("GSSAPI did not provide confidentiality")));
+		ret = -1;
+		goto cleanup;
+	}
+
+	netlen = htonl(output.length);
+	memcpy(lenbuf, &netlen, 4);
+	iov[0].iov_base = lenbuf;
+	iov[0].iov_len = 4;
+	iov[1].iov_base = output.value;
+	iov[1].iov_len = output.length;
+	ret = writev(port->sock, iov, 2);
+	if (ret == output.length + 4)
+	{
+		/*
+		 * Strictly speaking, this isn't true; we did write more than `len`
+		 * bytes.  However, this information is actually used to keep track of
+		 * what has/hasn't been written yet, not actually report the number of
+		 * bytes we wrote.
+		 */
+		ret = len;
+		goto cleanup;
+	}
+	else if (ret < 0 && errno != EAGAIN && errno != EWOULDBLOCK)
+	{
+		ereport(FATAL, (errmsg("Failed to send entire GSSAPI blob")));
+		ret = -1;
+		goto cleanup;
+	}
+
+	if (ret < 4)
+	{
+		appendBinaryStringInfo(&port->gss->writebuf, lenbuf + ret, 4 - ret);
+		ret = 0;
+	}
+	else
+	{
+		ret -= 4;
+	}
+	appendBinaryStringInfo(&port->gss->writebuf, (char *)output.value + ret,
+						   output.length - ret);
+
+	/* Set return so that we get retried when the socket becomes writable */
+	ret = 0;
+ cleanup:
+	if (output.value != NULL)
+		gss_release_buffer(&minor, &output);
+
+	return ret;
+}
+
+static ssize_t
+be_gssapi_read_from_buffer(Port *port, void *ptr, size_t len)
+{
+	ssize_t ret = 0;
+
+	if (port->gss->buf.len > 4 && port->gss->buf.cursor < port->gss->buf.len)
+	{
+		if (len > port->gss->buf.len - port->gss->buf.cursor)
+			len = port->gss->buf.len - port->gss->buf.cursor;
+
+		memcpy(ptr, port->gss->buf.data + port->gss->buf.cursor, len);
+		port->gss->buf.cursor += len;
+
+		ret = len;
+	}
+
+	if (port->gss->buf.cursor == port->gss->buf.len)
+	{
+		port->gss->buf.cursor = port->gss->buf.len = 0;
+		port->gss->buf.data[0] = '\0';
+	}
+
+	return ret;
+}
+
+/*
+ * Here's how the buffering works:
+ *
+ * First, we read the packet into port->gss->buf.data.  The first four bytes
+ * of this will be the network-order length of the GSSAPI-encrypted blob; from
+ * position 4 to port->gss->buf.len is then this blob.  Therefore, at this
+ * point port->gss->buf.len is the length of the blob plus 4.
+ * port->gss->buf.cursor is zero for this entire step.
+ *
+ * Then we overwrite port->gss->buf.data entirely with the decrypted contents.
+ * At this point, port->gss->buf.len reflects the actual length of the
+ * decrypted data.  port->gss->buf.cursor is then used to incrementally return
+ * this data to the caller and is therefore nonzero during this step.
+ *
+ * Once all decrypted data is returned to the caller, the cycle repeats.
+ */
+ssize_t
+be_gssapi_read(Port *port, void *ptr, size_t len)
+{
+	OM_uint32 major, minor;
+	gss_buffer_desc input, output;
+	ssize_t ret;
+	int conf = 0;
+
+	ret = be_gssapi_should_crypto(port);
+	if (ret == -1)
+		return -1;
+	else if (ret == 0)
+		return secure_raw_read(port, ptr, len);
+
+	if (len == 0)
+		return 0;
+
+	if (port->gss->buf.cursor > 0)
+	{
+		ret = be_gssapi_read_from_buffer(port, ptr, len);
+		if (ret > 0)
+			return ret + be_gssapi_read(port, (char *)ptr + ret, len - ret);
+	}
+
+	/* our buffer is now empty */
+	if (port->gss->buf.len < 4)
+	{
+		enlargeStringInfo(&port->gss->buf, 4);
+		ret = secure_raw_read(port, port->gss->buf.data + port->gss->buf.len,
+							  4 - port->gss->buf.len);
+		if (ret < 0)
+			return ret;
+
+		port->gss->buf.len += ret;
+		port->gss->buf.data[port->gss->buf.len] = '\0';
+		if (port->gss->buf.len < 4)
+			return 0;
+	}
+
+	/* we know the length of the packet at this point */
+	memcpy((char *)&input.length, port->gss->buf.data, 4);
+	input.length = ntohl(input.length);
+	enlargeStringInfo(&port->gss->buf, input.length - port->gss->buf.len + 4);
+
+	ret = secure_raw_read(port, port->gss->buf.data + port->gss->buf.len,
+						  input.length - port->gss->buf.len + 4);
+	if (ret < 0)
+		return ret;
+
+	port->gss->buf.len += ret;
+	port->gss->buf.data[port->gss->buf.len] = '\0';
+	if (port->gss->buf.len - 4 < input.length)
+		return 0;
+
+	output.value = NULL;
+	output.length = 0;
+	input.value = port->gss->buf.data + 4;
+	major = gss_unwrap(&minor, port->gss->ctx, &input, &output, &conf, NULL);
+	if (GSS_ERROR(major))
+	{
+		pg_GSS_error(ERROR,
+					 gettext_noop("GSSAPI unwrap error"),
+					 major, minor);
+		ret = -1;
+		goto cleanup;
+	}
+	else if (conf == 0)
+	{
+		ereport(FATAL, (errmsg("GSSAPI did not provide confidentiality")));
+		ret = -1;
+		goto cleanup;
+	}
+
+	port->gss->buf.cursor = port->gss->buf.len = 0;
+	port->gss->buf.data[0] = '\0';
+	enlargeStringInfo(&port->gss->buf, output.length);
+	memcpy(port->gss->buf.data, output.value, output.length);
+	port->gss->buf.len = output.length;
+	port->gss->buf.data[port->gss->buf.len] = '\0';
+
+	ret = be_gssapi_read_from_buffer(port, ptr, len);
+ cleanup:
+	if (output.value != NULL)
+		gss_release_buffer(&minor, &output);
+
+	return ret;
+}
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index ac709d1..d43fa1b 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -132,6 +132,14 @@ retry:
 	}
 	else
 #endif
+#ifdef ENABLE_GSS
+	if (port->gss != NULL)
+	{
+		n = be_gssapi_read(port, ptr, len);
+		waitfor = WL_SOCKET_READABLE;
+	}
+	else
+#endif
 	{
 		n = secure_raw_read(port, ptr, len);
 		waitfor = WL_SOCKET_READABLE;
@@ -234,6 +242,14 @@ retry:
 	}
 	else
 #endif
+#ifdef ENABLE_GSS
+	if (port->gss != NULL)
+	{
+		n = be_gssapi_write(port, ptr, len);
+		waitfor = WL_SOCKET_WRITEABLE;
+	}
+	else
+#endif
 	{
 		n = secure_raw_write(port, ptr, len);
 		waitfor = WL_SOCKET_WRITEABLE;
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 9aaed5b..1d827ce 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -2349,6 +2349,10 @@ ConnCreate(int serverFd)
 		ExitPostmaster(1);
 	}
 #endif
+#ifdef ENABLE_GSS
+	initStringInfo(&port->gss->buf);
+	initStringInfo(&port->gss->writebuf);
+#endif
 #endif
 
 	return port;
@@ -2365,7 +2369,15 @@ ConnFree(Port *conn)
 	secure_close(conn);
 #endif
 	if (conn->gss)
+	{
+#ifdef ENABLE_GSS
+		if (conn->gss->buf.data)
+			pfree(conn->gss->buf.data);
+		if (conn->gss->writebuf.data)
+			pfree(conn->gss->writebuf.data);
+#endif
 		free(conn->gss);
+	}
 	free(conn);
 }
 
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 5d07b78..b0a31ae 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -68,6 +68,7 @@ typedef struct
 #include "datatype/timestamp.h"
 #include "libpq/hba.h"
 #include "libpq/pqcomm.h"
+#include "lib/stringinfo.h"
 
 
 typedef enum CAC_state
@@ -88,6 +89,9 @@ typedef struct
 	gss_cred_id_t cred;			/* GSSAPI connection cred's */
 	gss_ctx_id_t ctx;			/* GSSAPI connection context */
 	gss_name_t	name;			/* GSSAPI client name */
+	StringInfoData buf;			/* GSSAPI encryption data buffering */
+	bool should_encrypt;		/* GSSAPI encryption start */
+	StringInfoData writebuf;	/* GSSAPI nonblocking write buffering */
 #endif
 } pg_gssinfo;
 #endif
@@ -214,6 +218,33 @@ extern void be_tls_get_cipher(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peerdn_name(Port *port, char *ptr, size_t len);
 #endif
 
+#ifdef ENABLE_GSS
+int pg_GSS_recvauth(Port *port);
+ssize_t be_gssapi_read(Port *port, void *ptr, size_t len);
+ssize_t be_gssapi_write(Port *port, void *ptr, size_t len);
+
+/* GUC */
+extern char	   *pg_krb_server_keyfile;
+extern bool		pg_krb_caseins_users;
+
+/*
+ * Maximum accepted size of GSS and SSPI authentication tokens.
+ *
+ * Kerberos tickets are usually quite small, but the TGTs issued by Windows
+ * domain controllers include an authorization field known as the Privilege
+ * Attribute Certificate (PAC), which contains the user's Windows permissions
+ * (group memberships etc.). The PAC is copied into all tickets obtained on
+ * the basis of this TGT (even those issued by Unix realms which the Windows
+ * realm trusts), and can be several kB in size. The maximum token size
+ * accepted by Windows systems is determined by the MaxAuthToken Windows
+ * registry setting. Microsoft recommends that it is not set higher than
+ * 65535 bytes, so that seems like a reasonable limit for us as well.
+ */
+#define PG_MAX_AUTH_TOKEN_LENGTH       65535
+#endif
+
+extern void sendAuthRequest(Port *port, AuthRequest areq);
+
 extern ProtocolVersion FrontendProtocol;
 
 /* TCP keepalives configuration. These are no-ops on an AF_UNIX socket. */
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index 1b292d2..4b206b4 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_gssapi),yes)
+OBJS += fe-gssapi.o
+endif
+
 ifeq ($(PORTNAME), cygwin)
 override shlib = cyg$(NAME)$(DLSUFFIX)
 endif
diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c
index cd863a5..c50f6dd 100644
--- a/src/interfaces/libpq/fe-auth.c
+++ b/src/interfaces/libpq/fe-auth.c
@@ -42,188 +42,6 @@
 #include "fe-auth.h"
 #include "libpq/md5.h"
 
-
-#ifdef ENABLE_GSS
-/*
- * GSSAPI authentication system.
- */
-
-#if defined(WIN32) && !defined(WIN32_ONLY_COMPILER)
-/*
- * MIT Kerberos GSSAPI DLL doesn't properly export the symbols for MingW
- * that contain the OIDs required. Redefine here, values copied
- * from src/athena/auth/krb5/src/lib/gssapi/generic/gssapi_generic.c
- */
-static const gss_OID_desc GSS_C_NT_HOSTBASED_SERVICE_desc =
-{10, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04"};
-static GSS_DLLIMP gss_OID GSS_C_NT_HOSTBASED_SERVICE = &GSS_C_NT_HOSTBASED_SERVICE_desc;
-#endif
-
-/*
- * Fetch all errors of a specific type and append to "str".
- */
-static void
-pg_GSS_error_int(PQExpBuffer str, const char *mprefix,
-				 OM_uint32 stat, int type)
-{
-	OM_uint32	lmin_s;
-	gss_buffer_desc lmsg;
-	OM_uint32	msg_ctx = 0;
-
-	do
-	{
-		gss_display_status(&lmin_s, stat, type,
-						   GSS_C_NO_OID, &msg_ctx, &lmsg);
-		appendPQExpBuffer(str, "%s: %s\n", mprefix, (char *) lmsg.value);
-		gss_release_buffer(&lmin_s, &lmsg);
-	} while (msg_ctx);
-}
-
-/*
- * GSSAPI errors contain two parts; put both into conn->errorMessage.
- */
-static void
-pg_GSS_error(const char *mprefix, PGconn *conn,
-			 OM_uint32 maj_stat, OM_uint32 min_stat)
-{
-	resetPQExpBuffer(&conn->errorMessage);
-
-	/* Fetch major error codes */
-	pg_GSS_error_int(&conn->errorMessage, mprefix, maj_stat, GSS_C_GSS_CODE);
-
-	/* Add the minor codes as well */
-	pg_GSS_error_int(&conn->errorMessage, mprefix, min_stat, GSS_C_MECH_CODE);
-}
-
-/*
- * Continue GSS authentication with next token as needed.
- */
-static int
-pg_GSS_continue(PGconn *conn)
-{
-	OM_uint32	maj_stat,
-				min_stat,
-				lmin_s;
-
-	maj_stat = gss_init_sec_context(&min_stat,
-									GSS_C_NO_CREDENTIAL,
-									&conn->gctx,
-									conn->gtarg_nam,
-									GSS_C_NO_OID,
-									GSS_C_MUTUAL_FLAG,
-									0,
-									GSS_C_NO_CHANNEL_BINDINGS,
-		  (conn->gctx == GSS_C_NO_CONTEXT) ? GSS_C_NO_BUFFER : &conn->ginbuf,
-									NULL,
-									&conn->goutbuf,
-									NULL,
-									NULL);
-
-	if (conn->gctx != GSS_C_NO_CONTEXT)
-	{
-		free(conn->ginbuf.value);
-		conn->ginbuf.value = NULL;
-		conn->ginbuf.length = 0;
-	}
-
-	if (conn->goutbuf.length != 0)
-	{
-		/*
-		 * GSS generated data to send to the server. We don't care if it's the
-		 * first or subsequent packet, just send the same kind of password
-		 * packet.
-		 */
-		if (pqPacketSend(conn, 'p',
-						 conn->goutbuf.value, conn->goutbuf.length)
-			!= STATUS_OK)
-		{
-			gss_release_buffer(&lmin_s, &conn->goutbuf);
-			return STATUS_ERROR;
-		}
-	}
-	gss_release_buffer(&lmin_s, &conn->goutbuf);
-
-	if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
-	{
-		pg_GSS_error(libpq_gettext("GSSAPI continuation error"),
-					 conn,
-					 maj_stat, min_stat);
-		gss_release_name(&lmin_s, &conn->gtarg_nam);
-		if (conn->gctx)
-			gss_delete_sec_context(&lmin_s, &conn->gctx, GSS_C_NO_BUFFER);
-		return STATUS_ERROR;
-	}
-
-	if (maj_stat == GSS_S_COMPLETE)
-		gss_release_name(&lmin_s, &conn->gtarg_nam);
-
-	return STATUS_OK;
-}
-
-/*
- * Send initial GSS authentication token
- */
-static int
-pg_GSS_startup(PGconn *conn)
-{
-	OM_uint32	maj_stat,
-				min_stat;
-	int			maxlen;
-	gss_buffer_desc temp_gbuf;
-
-	if (!(conn->pghost && conn->pghost[0] != '\0'))
-	{
-		printfPQExpBuffer(&conn->errorMessage,
-						  libpq_gettext("host name must be specified\n"));
-		return STATUS_ERROR;
-	}
-
-	if (conn->gctx)
-	{
-		printfPQExpBuffer(&conn->errorMessage,
-					libpq_gettext("duplicate GSS authentication request\n"));
-		return STATUS_ERROR;
-	}
-
-	/*
-	 * Import service principal name so the proper ticket can be acquired by
-	 * the GSSAPI system.
-	 */
-	maxlen = NI_MAXHOST + strlen(conn->krbsrvname) + 2;
-	temp_gbuf.value = (char *) malloc(maxlen);
-	if (!temp_gbuf.value)
-	{
-		printfPQExpBuffer(&conn->errorMessage,
-						  libpq_gettext("out of memory\n"));
-		return STATUS_ERROR;
-	}
-	snprintf(temp_gbuf.value, maxlen, "%s@%s",
-			 conn->krbsrvname, conn->pghost);
-	temp_gbuf.length = strlen(temp_gbuf.value);
-
-	maj_stat = gss_import_name(&min_stat, &temp_gbuf,
-							   GSS_C_NT_HOSTBASED_SERVICE, &conn->gtarg_nam);
-	free(temp_gbuf.value);
-
-	if (maj_stat != GSS_S_COMPLETE)
-	{
-		pg_GSS_error(libpq_gettext("GSSAPI name import error"),
-					 conn,
-					 maj_stat, min_stat);
-		return STATUS_ERROR;
-	}
-
-	/*
-	 * Initial packet is the same as a continuation packet with no initial
-	 * context.
-	 */
-	conn->gctx = GSS_C_NO_CONTEXT;
-
-	return pg_GSS_continue(conn);
-}
-#endif   /* ENABLE_GSS */
-
-
 #ifdef ENABLE_SSPI
 /*
  * SSPI authentication system (Windows only)
diff --git a/src/interfaces/libpq/fe-auth.h b/src/interfaces/libpq/fe-auth.h
index 9d11654..410c731 100644
--- a/src/interfaces/libpq/fe-auth.h
+++ b/src/interfaces/libpq/fe-auth.h
@@ -21,4 +21,9 @@
 extern int	pg_fe_sendauth(AuthRequest areq, PGconn *conn);
 extern char *pg_fe_getauthname(PQExpBuffer errorMessage);
 
+#ifdef ENABLE_GSS
+int pg_GSS_continue(PGconn *conn);
+int pg_GSS_startup(PGconn *conn);
+#endif
+
 #endif   /* FE_AUTH_H */
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 5ad4755..aa2340a 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -2798,6 +2798,10 @@ makeEmptyPGconn(void)
 	conn->wait_ssl_try = false;
 	conn->ssl_in_use = false;
 #endif
+#ifdef ENABLE_GSS
+	initPQExpBuffer(&conn->gbuf);
+	initPQExpBuffer(&conn->gwritebuf);
+#endif
 
 	/*
 	 * We try to send at least 8K at a time, which is the usual size of pipe
@@ -2914,6 +2918,10 @@ freePGconn(PGconn *conn)
 	if (conn->krbsrvname)
 		free(conn->krbsrvname);
 #endif
+#if defined(ENABLE_GSS)
+	termPQExpBuffer(&conn->gbuf);
+	termPQExpBuffer(&conn->gwritebuf);
+#endif
 #if defined(ENABLE_GSS) && defined(ENABLE_SSPI)
 	if (conn->gsslib)
 		free(conn->gsslib);
@@ -3019,6 +3027,8 @@ closePGconn(PGconn *conn)
 			gss_release_buffer(&min_s, &conn->ginbuf);
 		if (conn->goutbuf.length)
 			gss_release_buffer(&min_s, &conn->goutbuf);
+		resetPQExpBuffer(&conn->gbuf);
+		resetPQExpBuffer(&conn->gwritebuf);
 	}
 #endif
 #ifdef ENABLE_SSPI
diff --git a/src/interfaces/libpq/fe-gssapi.c b/src/interfaces/libpq/fe-gssapi.c
new file mode 100644
index 0000000..3427eb6
--- /dev/null
+++ b/src/interfaces/libpq/fe-gssapi.c
@@ -0,0 +1,475 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-auth.c
+ *	   The front-end (client) support for GSSAPI
+ *
+ * Portions Copyright (c) 2015-2016, Red Hat, Inc.
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/interfaces/libpq/fe-gssapi.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres_fe.h"
+
+#include "libpq-fe.h"
+#include "libpq-int.h"
+#include "fe-auth.h"
+
+#if defined(WIN32) && !defined(WIN32_ONLY_COMPILER)
+/*
+ * MIT Kerberos GSSAPI DLL doesn't properly export the symbols for MingW
+ * that contain the OIDs required. Redefine here, values copied
+ * from src/athena/auth/krb5/src/lib/gssapi/generic/gssapi_generic.c
+ */
+static const gss_OID_desc GSS_C_NT_HOSTBASED_SERVICE_desc =
+{10, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04"};
+static GSS_DLLIMP gss_OID GSS_C_NT_HOSTBASED_SERVICE = &GSS_C_NT_HOSTBASED_SERVICE_desc;
+#endif
+
+/*
+ * Fetch all errors of a specific type and append to "str".
+ */
+static void
+pg_GSS_error_int(PQExpBuffer str, const char *mprefix,
+				 OM_uint32 stat, int type)
+{
+	OM_uint32	lmin_s;
+	gss_buffer_desc lmsg;
+	OM_uint32	msg_ctx = 0;
+
+	do
+	{
+		gss_display_status(&lmin_s, stat, type,
+						   GSS_C_NO_OID, &msg_ctx, &lmsg);
+		appendPQExpBuffer(str, "%s: %s\n", mprefix, (char *) lmsg.value);
+		gss_release_buffer(&lmin_s, &lmsg);
+	} while (msg_ctx);
+}
+
+/*
+ * GSSAPI errors contain two parts; put both into conn->errorMessage.
+ */
+static void
+pg_GSS_error(const char *mprefix, PGconn *conn,
+			 OM_uint32 maj_stat, OM_uint32 min_stat)
+{
+	resetPQExpBuffer(&conn->errorMessage);
+
+	/* Fetch major error codes */
+	pg_GSS_error_int(&conn->errorMessage, mprefix, maj_stat, GSS_C_GSS_CODE);
+
+	/* Add the minor codes as well */
+	pg_GSS_error_int(&conn->errorMessage, mprefix, min_stat, GSS_C_MECH_CODE);
+}
+
+/*
+ * Continue GSS authentication with next token as needed.
+ */
+int
+pg_GSS_continue(PGconn *conn)
+{
+	OM_uint32	maj_stat,
+				min_stat,
+				lmin_s;
+
+	maj_stat = gss_init_sec_context(&min_stat,
+									GSS_C_NO_CREDENTIAL,
+									&conn->gctx,
+									conn->gtarg_nam,
+									GSS_C_NO_OID,
+									GSS_C_MUTUAL_FLAG,
+									0,
+									GSS_C_NO_CHANNEL_BINDINGS,
+		  (conn->gctx == GSS_C_NO_CONTEXT) ? GSS_C_NO_BUFFER : &conn->ginbuf,
+									NULL,
+									&conn->goutbuf,
+									NULL,
+									NULL);
+
+	if (conn->gctx != GSS_C_NO_CONTEXT)
+	{
+		free(conn->ginbuf.value);
+		conn->ginbuf.value = NULL;
+		conn->ginbuf.length = 0;
+	}
+
+	if (conn->goutbuf.length != 0)
+	{
+		/*
+		 * GSS generated data to send to the server. We don't care if it's the
+		 * first or subsequent packet, just send the same kind of password
+		 * packet.
+		 */
+		if (pqPacketSend(conn, 'p',
+						 conn->goutbuf.value, conn->goutbuf.length)
+			!= STATUS_OK)
+		{
+			gss_release_buffer(&lmin_s, &conn->goutbuf);
+			return STATUS_ERROR;
+		}
+	}
+	gss_release_buffer(&lmin_s, &conn->goutbuf);
+
+	if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
+	{
+		pg_GSS_error(libpq_gettext("GSSAPI continuation error"),
+					 conn,
+					 maj_stat, min_stat);
+		gss_release_name(&lmin_s, &conn->gtarg_nam);
+		if (conn->gctx)
+			gss_delete_sec_context(&lmin_s, &conn->gctx, GSS_C_NO_BUFFER);
+		return STATUS_ERROR;
+	}
+
+	if (maj_stat == GSS_S_COMPLETE)
+		gss_release_name(&lmin_s, &conn->gtarg_nam);
+
+	return STATUS_OK;
+}
+
+/*
+ * Send initial GSS authentication token
+ */
+int
+pg_GSS_startup(PGconn *conn)
+{
+	OM_uint32	maj_stat,
+				min_stat;
+	int			maxlen;
+	gss_buffer_desc temp_gbuf;
+
+	if (!(conn->pghost && conn->pghost[0] != '\0'))
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("host name must be specified\n"));
+		return STATUS_ERROR;
+	}
+
+	if (conn->gctx)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+					libpq_gettext("duplicate GSS authentication request\n"));
+		return STATUS_ERROR;
+	}
+
+	/*
+	 * Import service principal name so the proper ticket can be acquired by
+	 * the GSSAPI system.
+	 */
+	maxlen = NI_MAXHOST + strlen(conn->krbsrvname) + 2;
+	temp_gbuf.value = (char *) malloc(maxlen);
+	if (!temp_gbuf.value)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("out of memory\n"));
+		return STATUS_ERROR;
+	}
+	snprintf(temp_gbuf.value, maxlen, "%s@%s",
+			 conn->krbsrvname, conn->pghost);
+	temp_gbuf.length = strlen(temp_gbuf.value);
+
+	maj_stat = gss_import_name(&min_stat, &temp_gbuf,
+							   GSS_C_NT_HOSTBASED_SERVICE, &conn->gtarg_nam);
+	free(temp_gbuf.value);
+
+	if (maj_stat != GSS_S_COMPLETE)
+	{
+		pg_GSS_error(libpq_gettext("GSSAPI name import error"),
+					 conn,
+					 maj_stat, min_stat);
+		return STATUS_ERROR;
+	}
+
+	/*
+	 * Initial packet is the same as a continuation packet with no initial
+	 * context.
+	 */
+	conn->gctx = GSS_C_NO_CONTEXT;
+
+	return pg_GSS_continue(conn);
+}
+
+/*
+ * Only consider encryption when GSS context is complete
+ */
+static ssize_t
+pg_GSS_should_crypto(PGconn *conn)
+{
+	OM_uint32 major, minor;
+	int open = 1;
+
+	if (conn->gctx == GSS_C_NO_CONTEXT)
+		return 0;
+	else if (conn->gencrypt)
+		return 1;
+
+	major = gss_inquire_context(&minor, conn->gctx,
+								NULL, NULL, NULL, NULL, NULL, NULL,
+								&open);
+	if (major == GSS_S_NO_CONTEXT)
+	{
+		/*
+         * In MIT krb5 < 1.14, it was not possible to call gss_inquire_context
+         * on an incomplete context.  This was a violation of rfc2744 and has
+         * been corrected in https://github.com/krb5/krb5/pull/285
+         */
+		return 0;
+	}
+	else if (GSS_ERROR(major))
+	{
+		pg_GSS_error(libpq_gettext("GSSAPI context state error"), conn,
+					 major, minor);
+		return -1;
+	}
+	else if (open != 0)
+	{
+		conn->gencrypt = true;
+		return 1;
+	}
+	return 0;
+}
+
+ssize_t
+pg_GSS_write(PGconn *conn, void *ptr, size_t len)
+{
+	OM_uint32 major, minor;
+	gss_buffer_desc input, output;
+	ssize_t ret;
+	int conf;
+	uint32 netlen;
+	char lenbuf[4];
+	struct iovec iov[2];
+
+	ret = pg_GSS_should_crypto(conn);
+	if (ret == -1)
+		return -1;
+	else if (ret == 0)
+		return pqsecure_raw_write(conn, ptr, len);
+
+	if (conn->gwritebuf.len != 0)
+	{
+		ret = send(conn->sock, conn->gwritebuf.data + conn->gwritecurs,
+				   conn->gwritebuf.len - conn->gwritecurs, 0);
+		if (ret < 0)
+			return ret;
+		conn->gwritecurs += ret;
+		if (conn->gwritecurs == conn->gwritebuf.len)
+		{
+			conn->gwritebuf.len = conn->gwritecurs = 0;
+			conn->gwritebuf.data[0] = '\0';
+			/* The entire request has now been written */
+			return len;
+		}
+		/* need to be called again */
+		return 0;
+	}
+
+	output.value = NULL;
+	output.length = 0;
+
+	input.value = ptr;
+	input.length = len;
+
+	conf = 0;
+	major = gss_wrap(&minor, conn->gctx, 1, GSS_C_QOP_DEFAULT,
+					 &input, &conf, &output);
+	if (GSS_ERROR(major))
+	{
+		pg_GSS_error(libpq_gettext("GSSAPI wrap error"), conn,
+					 major, minor);
+		ret = -1;
+		goto cleanup;
+	}
+	else if (conf == 0)
+	{
+		printfPQExpBuffer(&conn->errorMessage, libpq_gettext(
+							  "GSSAPI did not provide confidentiality\n"));
+		ret = -1;
+		goto cleanup;
+	}
+
+	netlen = htonl(output.length);
+	memcpy(lenbuf, &netlen, 4);
+	iov[0].iov_base = lenbuf;
+	iov[0].iov_len = 4;
+	iov[1].iov_base = output.value;
+	iov[1].iov_len = output.length;
+	errno = 0;
+	ret = writev(conn->sock, iov, 2);
+	if (ret == output.length + 4)
+	{
+		/*
+		 * pqsecure_write expects the return value, when >= 0, to be the
+		 * number bytes from ptr delivered, not the number of bytes actually
+		 * written to socket.
+		 */
+		ret = len;
+		goto cleanup;
+	}
+	else if (ret < 0 && errno != EAGAIN && errno != EWOULDBLOCK)
+	{
+		printfPQExpBuffer(&conn->errorMessage, libpq_gettext(
+							  "GSSAPI writev() failed to send everything\n"));
+		ret = -1;
+		goto cleanup;
+	}
+
+	if (ret < 4)
+	{
+		appendBinaryPQExpBuffer(&conn->gwritebuf, lenbuf + ret, 4 - ret);
+		ret = 0;
+	}
+	else
+	{
+		ret -= 4;
+	}
+	appendBinaryPQExpBuffer(&conn->gwritebuf, (char *)output.value + 4 - ret,
+							output.length + 4 - ret);
+
+	/* Set return so that we get retried when the socket becomes writable */
+	ret = 0;
+ cleanup:
+	if (output.value != NULL)
+		gss_release_buffer(&minor, &output);
+
+	return ret;
+}
+
+static ssize_t
+pg_GSS_read_from_buffer(PGconn *conn, void *ptr, size_t len)
+{
+	ssize_t ret = 0;
+
+	if (conn->gcursor < conn->gbuf.len)
+	{
+		if (len > conn->gbuf.len - conn->gcursor)
+			len = conn->gbuf.len - conn->gcursor;
+
+		memcpy(ptr, conn->gbuf.data + conn->gcursor, len);
+		conn->gcursor += len;
+		ret = len;
+	}
+
+	if (conn->gcursor == conn->gbuf.len)
+	{
+		conn->gcursor = conn->gbuf.len = 0;
+		conn->gbuf.data[0] = '\0';
+	}
+
+	return ret;
+}
+
+/*
+ * Buffering behaves as in be_gssapi_read (in be-gssapi.c).  Because this is
+ * the frontend, we use a PQExpBuffer at conn->gbuf instead of a StringInfo,
+ * and so there is an additional, separate cursor field in the structure.
+ */
+ssize_t
+pg_GSS_read(PGconn *conn, void *ptr, size_t len)
+{
+	OM_uint32 major, minor;
+	gss_buffer_desc input, output;
+	ssize_t ret;
+	int conf = 0;
+
+	ret = pg_GSS_should_crypto(conn);
+	if (ret == -1)
+		return -1;
+	else if (ret == 0)
+		return pqsecure_raw_read(conn, ptr, len);
+
+	if (len == 0)
+		return 0;
+
+	if (conn->gcursor > 0)
+	{
+		ret = pg_GSS_read_from_buffer(conn, ptr, len);
+		if (ret > 0)
+			return ret + pg_GSS_read(conn, (char *)ptr + ret, len - ret);
+	}
+
+	/* our buffer is now empty */
+	if (conn->gbuf.len < 4)
+	{
+		ret = enlargePQExpBuffer(&conn->gbuf, 4);
+		if (ret != 1)
+		{
+			printfPQExpBuffer(&conn->errorMessage, libpq_gettext(
+								  "Failed to fit packet length in buffer\n"));
+			return -1;
+		}
+		ret = pqsecure_raw_read(conn, conn->gbuf.data, 4);
+		if (ret < 0)
+			/* error already set by secure_raw_read */
+			return ret;
+		conn->gbuf.len += ret;
+		conn->gbuf.data[conn->gbuf.len] = '\0';
+		if (conn->gbuf.len < 4)
+			return 0;
+	}
+
+	/* we know the length of the packet at this point */
+	memcpy((char *)&input.length, conn->gbuf.data, 4);
+	input.length = ntohl(input.length);
+	ret = enlargePQExpBuffer(&conn->gbuf, input.length - conn->gbuf.len + 4);
+	if (ret != 1)
+	{
+		printfPQExpBuffer(&conn->errorMessage, libpq_gettext(
+							  "GSSAPI encrypted packet (length %ld) too big\n"),
+						  input.length);
+		return -1;
+	}
+
+	ret = pqsecure_raw_read(conn, conn->gbuf.data + conn->gbuf.len,
+							input.length - conn->gbuf.len + 4);
+	if (ret < 0)
+		return ret;
+	conn->gbuf.len += ret;
+	conn->gbuf.data[conn->gbuf.len] = '\0';
+	if (conn->gbuf.len - 4 < input.length)
+		return 0;
+
+	output.value = NULL;
+	output.length = 0;
+	input.value = conn->gbuf.data + 4;
+	major = gss_unwrap(&minor, conn->gctx, &input, &output, &conf, NULL);
+	if (GSS_ERROR(major))
+	{
+		pg_GSS_error(libpq_gettext("GSSAPI unwrap error"), conn,
+					 major, minor);
+		ret = -1;
+		goto cleanup;
+	}
+	else if (conf == 0)
+	{
+		printfPQExpBuffer(&conn->errorMessage, libpq_gettext(
+							  "GSSAPI did not provide confidentiality\n"));
+		ret = -1;
+		goto cleanup;
+	}
+
+	conn->gcursor = conn->gbuf.len = 0;
+	conn->gbuf.data[0] = '\0';
+	ret = enlargePQExpBuffer(&conn->gbuf, output.length);
+	if (ret != 1)
+	{
+		printfPQExpBuffer(&conn->errorMessage, libpq_gettext(
+							  "GSSAPI decrypted packet (length %ld) too big\n"),
+						  output.length);
+		return -1;
+	}
+	memcpy(conn->gbuf.data, output.value, output.length);
+	conn->gbuf.len = output.length;
+	conn->gbuf.data[conn->gbuf.len] = '\0';
+
+	ret = pg_GSS_read_from_buffer(conn, ptr, len);
+
+ cleanup:
+	if (output.value != NULL)
+		gss_release_buffer(&minor, &output);
+	return ret;
+}
diff --git a/src/interfaces/libpq/fe-secure.c b/src/interfaces/libpq/fe-secure.c
index 94e47a5..14fba1f 100644
--- a/src/interfaces/libpq/fe-secure.c
+++ b/src/interfaces/libpq/fe-secure.c
@@ -213,6 +213,13 @@ pqsecure_read(PGconn *conn, void *ptr, size_t len)
 	}
 	else
 #endif
+#ifdef ENABLE_GSS
+	if (conn->gctx != GSS_C_NO_CONTEXT)
+	{
+		n = pg_GSS_read(conn, ptr, len);
+	}
+	else
+#endif
 	{
 		n = pqsecure_raw_read(conn, ptr, len);
 	}
@@ -279,7 +286,7 @@ pqsecure_raw_read(PGconn *conn, void *ptr, size_t len)
  * to determine whether to continue/retry after error.
  */
 ssize_t
-pqsecure_write(PGconn *conn, const void *ptr, size_t len)
+pqsecure_write(PGconn *conn, void *ptr, size_t len)
 {
 	ssize_t		n;
 
@@ -290,6 +297,13 @@ pqsecure_write(PGconn *conn, const void *ptr, size_t len)
 	}
 	else
 #endif
+#ifdef ENABLE_GSS
+	if (conn->gctx != GSS_C_NO_CONTEXT)
+	{
+		n = pg_GSS_write(conn, ptr, len);
+	}
+	else
+#endif
 	{
 		n = pqsecure_raw_write(conn, ptr, len);
 	}
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 6c9bbf7..6b75d104 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -23,6 +23,7 @@
 /* We assume libpq-fe.h has already been included. */
 #include "postgres_fe.h"
 #include "libpq-events.h"
+#include "lib/stringinfo.h"
 
 #include <time.h>
 #include <sys/types.h>
@@ -445,6 +446,11 @@ struct pg_conn
 	gss_name_t	gtarg_nam;		/* GSS target name */
 	gss_buffer_desc ginbuf;		/* GSS input token */
 	gss_buffer_desc goutbuf;	/* GSS output token */
+	PQExpBufferData gbuf;		/* GSS encryption buffering */
+	size_t gcursor;				/* GSS buffering position */
+	bool gencrypt;				/* GSS is ready for encryption */
+	PQExpBufferData gwritebuf;	/* GSS nonblocking write buffering */
+	size_t gwritecurs;			/* GSS write buffer position */
 #endif
 
 #ifdef ENABLE_SSPI
@@ -620,7 +626,7 @@ extern void pqsecure_destroy(void);
 extern PostgresPollingStatusType pqsecure_open_client(PGconn *);
 extern void pqsecure_close(PGconn *);
 extern ssize_t pqsecure_read(PGconn *, void *ptr, size_t len);
-extern ssize_t pqsecure_write(PGconn *, const void *ptr, size_t len);
+extern ssize_t pqsecure_write(PGconn *, 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);
 
@@ -642,6 +648,14 @@ extern bool pgtls_read_pending(PGconn *conn);
 extern ssize_t pgtls_write(PGconn *conn, const void *ptr, size_t len);
 
 /*
+ * The GSSAPI backend in fe-gssapi.c provides these functions.
+ */
+#ifdef ENABLE_GSS
+extern ssize_t pg_GSS_read(PGconn *conn, void *ptr, size_t len);
+extern ssize_t pg_GSS_write(PGconn *conn, void *ptr, size_t len);
+#endif
+
+/*
  * this is so that we can check if a connection is non-blocking internally
  * without the overhead of a function call
  */
-- 
2.7.0

Attachment: signature.asc
Description: PGP signature

Reply via email to