diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml
index c7e4d3817f10d8c3a6f1d61203e54bb6ef231f18..45a3cf3defd0d91a91cb3d51854ede43623b3d72 100644
--- a/doc/src/sgml/client-auth.sgml
+++ b/doc/src/sgml/client-auth.sgml
@@ -108,6 +108,8 @@ hostnossl  <replaceable>database</replaceable>  <replaceable>user</replaceable>
 host       <replaceable>database</replaceable>  <replaceable>user</replaceable>  <replaceable>IP-address</replaceable>  <replaceable>IP-mask</replaceable>  <replaceable>auth-method</replaceable>  <optional><replaceable>auth-options</replaceable></optional>
 hostssl    <replaceable>database</replaceable>  <replaceable>user</replaceable>  <replaceable>IP-address</replaceable>  <replaceable>IP-mask</replaceable>  <replaceable>auth-method</replaceable>  <optional><replaceable>auth-options</replaceable></optional>
 hostnossl  <replaceable>database</replaceable>  <replaceable>user</replaceable>  <replaceable>IP-address</replaceable>  <replaceable>IP-mask</replaceable>  <replaceable>auth-method</replaceable>  <optional><replaceable>auth-options</replaceable></optional>
+hostgssenc <replaceable>database</replaceable>  <replaceable>user</replaceable>  <replaceable>IP-address</replaceable>  <replaceable>IP-mask</replaceable>  <replaceable>auth-method</replaceable>  <optional><replaceable>auth-options</replaceable></optional>
+hostnogssenc <replaceable>database</replaceable>  <replaceable>user</replaceable>  <replaceable>IP-address</replaceable>  <replaceable>IP-mask</replaceable>  <replaceable>auth-method</replaceable>  <optional><replaceable>auth-options</replaceable></optional>
 </synopsis>
    The meaning of the fields is as follows:
 
@@ -128,9 +130,10 @@ hostnossl  <replaceable>database</replaceable>  <replaceable>user</replaceable>
      <listitem>
       <para>
        This record matches connection attempts made using TCP/IP.
-       <literal>host</literal> records match either
+       <literal>host</literal> records match
        <acronym>SSL</acronym> or non-<acronym>SSL</acronym> connection
-       attempts.
+       attempts as well as <acronym>GSSAPI</acronym> encrypted or
+       non-<acronym>GSSAPI</acronym> encrypted connection attempts.
       </para>
      <note>
       <para>
@@ -176,6 +179,42 @@ hostnossl  <replaceable>database</replaceable>  <replaceable>user</replaceable>
      </listitem>
     </varlistentry>
 
+    <varlistentry>
+     <term><literal>hostgssenc</literal></term>
+     <listitem>
+      <para>
+       This record matches connection attempts made using TCP/IP,
+       but only when the connection is made with <acronym>GSSAPI</acronym>
+       encryption.
+      </para>
+
+      <para>
+       To make use of this option the server must be built with
+       <acronym>GSSAPI</acronym> support.  Otherwise,
+       the <literal>hostgssenc</literal> record is ignored except for logging
+       a warning that it cannot match any connections.
+      </para>
+
+      <para>
+        Note that the only supported <xref linkend="auth-methods"/> for use
+        with <acronym>GSSAPI</acronym> encryption
+        are <literal>gss</literal>, <literal>reject</literal>,
+        and <literal>trust</literal>.
+      </para>
+     </listitem>
+    </varlistentry>
+
+    <varlistentry>
+     <term><literal>hostnogssenc</literal></term>
+     <listitem>
+      <para>
+       This record type has the opposite behavior of <literal>hostgssenc</literal>;
+       it only matches connection attempts made over
+       TCP/IP that do not use <acronym>GSSAPI</acronym> encryption.
+      </para>
+     </listitem>
+    </varlistentry>
+
     <varlistentry>
      <term><replaceable>database</replaceable></term>
      <listitem>
@@ -450,8 +489,9 @@ hostnossl  <replaceable>database</replaceable>  <replaceable>user</replaceable>
         <listitem>
          <para>
           Use GSSAPI to authenticate the user. This is only
-          available for TCP/IP connections. See <xref
-          linkend="gssapi-auth"/> for details.
+          available for TCP/IP connections . See <xref
+          linkend="gssapi-auth"/> for details.  It can be used in conjunction
+          with GSSAPI encryption.
          </para>
         </listitem>
        </varlistentry>
@@ -703,15 +743,18 @@ host    postgres        all             192.168.12.10/32        scram-sha-256
 host    all             mike            .example.com            md5
 host    all             all             .example.com            scram-sha-256
 
-# In the absence of preceding "host" lines, these two lines will
+# In the absence of preceding "host" lines, these three lines will
 # reject all connections from 192.168.54.1 (since that entry will be
-# matched first), but allow GSSAPI connections from anywhere else
-# on the Internet.  The zero mask causes no bits of the host IP
-# address to be considered, so it matches any host.
+# matched first), but allow GSSAPI-encrypted connections from anywhere else
+# on the Internet.  The zero mask causes no bits of the host IP address to
+# be considered, so it matches any host.  Unencrypted GSSAPI connections 
+# (which "fall through" to the third line since "hostgssenc" only matches
+# encrypted GSSAPI connections) are allowed, but only from 192.168.12.10.  
 #
 # TYPE  DATABASE        USER            ADDRESS                 METHOD
 host    all             all             192.168.54.1/32         reject
-host    all             all             0.0.0.0/0               gss
+hostgssenc all          all             0.0.0.0/0               gss
+host    all             all             192.168.12.10/32        gss
 
 # Allow users from 192.168.x.x hosts to connect to any database, if
 # they pass the ident check.  If, for example, ident says the user is
@@ -1058,13 +1101,16 @@ omicron         bryanh                  guest1
    <para>
     <productname>GSSAPI</productname> is an industry-standard protocol
     for secure authentication defined in RFC 2743.
-    <productname>PostgreSQL</productname> supports
-    <productname>GSSAPI</productname> with <productname>Kerberos</productname>
-    authentication according to RFC 1964. <productname>GSSAPI</productname>
-    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.
+
+    <productname>PostgreSQL</productname>
+    supports <productname>GSSAPI</productname> for use as either an encrypted,
+    authenticated layer, or for authentication only.
+    <productname>GSSAPI</productname> provides automatic authentication
+    (single sign-on) for systems that support it. The authentication itself is
+    secure.  If <productname>GSSAPI</productname> encryption
+    (see <literal>hostgssenc</literal>) or <acronym>SSL</acronym> encryption are
+    used, the data sent along the database connection will be encrypted;
+    otherwise, it will not.
    </para>
 
    <para>
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index c1d1b6b2db344d1e93f370e54afa1da193cc3b83..0863a02411da6f5efef279108d02a0a177548359 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1316,6 +1316,63 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
       </listitem>
      </varlistentry>
 
+     <varlistentry id="libpq-connect-gssencmode" xreflabel="gssencmode">
+      <term><literal>gssencmode</literal></term>
+      <listitem>
+       <para>
+        This option determines whether or with what priority a secure
+        <acronym>GSS</acronym> TCP/IP connection will be negotiated with the
+        server. There are three modes:
+
+        <variablelist>
+         <varlistentry>
+          <term><literal>disable</literal></term>
+          <listitem>
+           <para>
+            only try a non-<acronym>GSSAPI</acronym>-encrypted connection
+           </para>
+          </listitem>
+         </varlistentry>
+
+         <varlistentry>
+          <term><literal>prefer</literal> (default)</term>
+          <listitem>
+           <para>
+            if there are <acronym>GSSAPI</acronym> credentials present (i.e.,
+            in a credentials cache), first try
+            a <acronym>GSSAPI</acronym>-encrypted connection; if that fails or
+            there are no credentials, try a
+            non-<acronym>GSSAPI</acronym>-encrypted connection.  This is the
+            default when <productname>PostgreSQL</productname> has been
+            compiled with <acronym>GSSAPI</acronym> support.
+           </para>
+          </listitem>
+         </varlistentry>
+
+         <varlistentry>
+          <term><literal>require</literal></term>
+          <listitem>
+           <para>
+            only try a <acronym>GSSAPI</acronym>-encrypted connection
+           </para>
+          </listitem>
+         </varlistentry>
+        </variablelist>
+       </para>
+
+       <para>
+        <literal>gssencmode</literal> is ignored for Unix domain socket
+        communication.  If <productname>PostgreSQL</productname> is compiled
+        without GSSAPI support, using the <literal>require</literal> option
+        will cause an error, while <literal>prefer</literal> will be accepted
+        but <application>libpq</application> will not actually attempt
+        a <acronym>GSSAPI</acronym>-encrypted
+        connection.<indexterm><primary>GSSAPI</primary><secondary sortas="libpq">with
+        libpq</secondary></indexterm>
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="libpq-connect-sslmode" xreflabel="sslmode">
       <term><literal>sslmode</literal></term>
       <listitem>
@@ -7948,7 +8005,7 @@ ldap://ldap.acme.com/cn=dbserver,cn=hosts?pgconnectinfo?base?(objectclass=*)
   </para>
 
   <para>
-   For a connection to be known secure, SSL usage must be configured
+   For a connection to be known SSL-secured, SSL usage must be configured
    on <emphasis>both the client and the server</emphasis> before the connection
    is made. If it is only configured on the server, the client may end up
    sending sensitive information (e.g. passwords) before
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 6679260508200d9b01073bc032e1d22e079e26ab..b946e13fdc377efe5343d1edc65a1fe0f2fa3eb8 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -336,6 +336,14 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
       </entry>
      </row>
 
+     <row>
+      <entry><structname>pg_stat_gssapi</structname><indexterm><primary>pg_stat_gssapi</primary></indexterm></entry>
+      <entry>One row per connection (regular and replication), showing information about
+       GSSAPI authentication and encryption used on this connection.
+       See <xref linkend="pg-stat-gssapi-view"/> for details.
+      </entry>
+     </row>
+
      <row>
       <entry><structname>pg_stat_progress_create_index</structname><indexterm><primary>pg_stat_progress_create_index</primary></indexterm></entry>
       <entry>One row for each backend running <command>CREATE INDEX</command>, showing
@@ -2281,6 +2289,55 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
    connection.
   </para>
 
+  <table id="pg-stat-gssapi-view" xreflabel="pg_stat_gssapi">
+   <title><structname>pg_stat_gssapi</structname> View</title>
+   <tgroup cols="3">
+    <thead>
+    <row>
+      <entry>Column</entry>
+      <entry>Type</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+   <tbody>
+    <row>
+     <entry><structfield>pid</structfield></entry>
+     <entry><type>integer</type></entry>
+     <entry>Process ID of a backend</entry>
+    </row>
+    <row>
+     <entry><structfield>gss_authenticated</structfield></entry>
+     <entry><type>boolean</type></entry>
+     <entry>True if GSSAPI authentication was used for this connection</entry>
+    </row>
+    <row>
+     <entry><structfield>principal</structfield></entry>
+     <entry><type>text</type></entry>
+     <entry>Principal used to authenticate this connection, or NULL
+      if GSSAPI was not used to authenticate this connection.  This
+      field is truncated if the principal is longer than
+      <symbol>NAMEDATALEN</symbol> (64 characters in a standard build).
+     </entry>
+    </row>
+    <row>
+     <entry><structfield>encrypted</structfield></entry>
+     <entry><type>boolean</type></entry>
+     <entry>True if GSSAPI encryption is in use on this connection</entry>
+    </row>
+   </tbody>
+   </tgroup>
+  </table>
+
+  <para>
+   The <structname>pg_stat_gssapi</structname> view will contain one row per
+   backend, showing information about GSSAPI usage on this connection. It can
+   be joined to <structname>pg_stat_activity</structname> or
+   <structname>pg_stat_replication</structname> on the
+   <structfield>pid</structfield> column to get more details about the
+   connection.
+  </para>
+
 
   <table id="pg-stat-archiver-view" xreflabel="pg_stat_archiver">
    <title><structname>pg_stat_archiver</structname> View</title>
diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml
index d786ebfb71dbe034583a6e2eef5aea7c824f0fd6..fde9dbc13468dca455ac62da168ff78ee3215227 100644
--- a/doc/src/sgml/runtime.sgml
+++ b/doc/src/sgml/runtime.sgml
@@ -2037,9 +2037,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
+   To prevent spoofing on TCP connections, either use
+   SSL certificates and make sure that clients check the server's certificate,
+   or use GSSAPI encryption (or both, if they're on separate connections).
+  </para>
+
+  <para>
+   To prevent spoofing with SSL, the server
    must be configured to accept only <literal>hostssl</literal> connections (<xref
    linkend="auth-pg-hba-conf"/>) and have SSL key and certificate files
    (<xref linkend="ssl-tcp"/>). The TCP client must connect using
@@ -2047,6 +2051,14 @@ pg_dumpall -p 5432 | psql -d postgres -p 5433
    <literal>verify-full</literal> and have the appropriate root certificate
    file installed (<xref linkend="libq-ssl-certificates"/>).
   </para>
+
+  <para>
+    To prevent spoofing with GSSAPI, the server must be configured to accept
+    only <literal>hostgssenc</literal> connections
+    (<xref linkend="auth-pg-hba-conf"/>) and use <literal>gss</literal>
+    authentication with them.  The TCP client must connect
+    using <literal>gssencmode=require</literal>.
+  </para>
  </sect1>
 
  <sect1 id="encryption-options">
@@ -2143,8 +2155,24 @@ pg_dumpall -p 5432 | psql -d postgres -p 5433
       which hosts can use non-encrypted connections (<literal>host</literal>)
       and which require SSL-encrypted connections
       (<literal>hostssl</literal>). Also, clients can specify that they
-      connect to servers only via SSL. <application>Stunnel</application> or
-      <application>SSH</application> can also be used to encrypt transmissions.
+      connect to servers only via SSL.
+     </para>
+
+     <para>
+      GSSAPI-encrypted connections encrypt all data sent across the network,
+      including queries and data returned.  (No password is sent across the
+      network.)  The <filename>pg_hba.conf</filename> file allows
+      administrators to specify which hosts can use non-encrypted connections
+      (<literal>host</literal>) and which require GSSAPI-encrypted connections
+      (<literal>hostgssenc</literal>).  Also, clients can specify that they
+      connect to servers only on GSSAPI-encrypted connections
+      (<literal>gssencmode=require</literal>).
+     </para>
+
+     <para>
+      <application>Stunnel</application> or
+      <application>SSH</application> can also be used to encrypt
+      transmissions.
      </para>
     </listitem>
   </varlistentry>
@@ -2561,6 +2589,45 @@ openssl x509 -req -in server.csr -text -days 365 \
 
  </sect1>
 
+ <sect1 id="gssapi-enc">
+  <title>Secure TCP/IP Connections with GSSAPI encryption</title>
+
+  <indexterm zone="gssapi-enc">
+   <primary>gssapi</primary>
+  </indexterm>
+
+  <para>
+   <productname>PostgreSQL</productname> also has native support for
+   using <acronym>GSSAPI</acronym> to encrypt client/server communications for
+   increased security.  Support requires that a <acronym>GSSAPI</acronym>
+   implementation (such as MIT krb5) is installed on both client and server
+   systems, and that support in <productname>PostgreSQL</productname> is
+   enabled at build time (see <xref linkend="installation"/>).
+  </para>
+
+  <sect2 id="gssapi-setup">
+   <title>Basic Setup</title>
+
+   <para>
+    The <productname>PostgreSQL</productname> server will listen for both
+    normal and <acronym>GSSAPI</acronym>-encrypted connections on the same TCP
+    port, and will negotiate with any connecting client on whether to
+    use <acronym>GSSAPI</acronym> for encryption (and for authentication).  By
+    default, this decision is up to the client (which means it can be
+    downgraded by an attacker); see <xref linkend="auth-pg-hba-conf"/> about
+    setting up the server to require the use of <acronym>GSSAPI</acronym> for
+    some or all conections.
+   </para>
+
+   <para>
+    Other than configuration of the negotiation
+    behavior, <acronym>GSSAPI</acronym> encryption requires no setup beyond
+    that which is necessary for GSSAPI authentication.  (For more information
+    on configuring that, see <xref linkend="gssapi-auth"/>.)
+   </para>
+  </sect2>
+ </sect1>
+
  <sect1 id="ssh-tunnels">
   <title>Secure TCP/IP Connections with <application>SSH</application> Tunnels</title>
 
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 3f2a7ef015850ececf328d66bda2c50d29ec6a70..72f786d6f8ad06a0806201246e0a4c5a14c92682 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -787,6 +787,14 @@ CREATE VIEW pg_stat_ssl AS
             S.ssl_issuer_dn AS issuer_dn
     FROM pg_stat_get_activity(NULL) AS S;
 
+CREATE VIEW pg_stat_gssapi AS
+    SELECT
+            S.pid,
+            S.gss_auth AS gss_authenticated,
+            S.gss_princ AS principal,
+            S.gss_enc AS encrypted
+    FROM pg_stat_get_activity(NULL) AS S;
+
 CREATE VIEW pg_replication_slots AS
     SELECT
             L.slot_name,
diff --git a/src/backend/libpq/Makefile b/src/backend/libpq/Makefile
index 3dbec23e30a7db6f10fa9777f4172c1f5c823c36..47efef0682de5aef9aaa0ac35f74018b0020bb4e 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-common.o be-secure-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 6f03c7c2a5ebe337978c0b115daa60d46a6a5528..62466be70235e42e289c5c6fa5fe13dd73439c12 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -36,6 +36,7 @@
 #include "port/pg_bswap.h"
 #include "replication/walsender.h"
 #include "storage/ipc.h"
+#include "utils/memutils.h"
 #include "utils/timestamp.h"
 
 
@@ -172,12 +173,9 @@ bool		pg_krb_caseins_users;
  *----------------------------------------------------------------
  */
 #ifdef ENABLE_GSS
-#if defined(HAVE_GSSAPI_H)
-#include <gssapi.h>
-#else
-#include <gssapi/gssapi.h>
-#endif
+#include "be-gssapi-common.h"
 
+static int	pg_GSS_checkauth(Port *port);
 static int	pg_GSS_recvauth(Port *port);
 #endif							/* ENABLE_GSS */
 
@@ -383,6 +381,17 @@ ClientAuthentication(Port *port)
 					 errmsg("connection requires a valid client certificate")));
 	}
 
+#ifdef ENABLE_GSS
+	if (port->gss->enc && port->hba->auth_method != uaReject &&
+		port->hba->auth_method != uaImplicitReject &&
+		port->hba->auth_method != uaTrust &&
+		port->hba->auth_method != uaGSS)
+	{
+		ereport(FATAL, (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
+						errmsg("GSSAPI encryption can only be used with gss, trust, or reject authentication methods")));
+	}
+#endif
+
 	/*
 	 * Now proceed to do the actual authentication check
 	 */
@@ -523,8 +532,14 @@ ClientAuthentication(Port *port)
 
 		case uaGSS:
 #ifdef ENABLE_GSS
-			sendAuthRequest(port, AUTH_REQ_GSS, NULL, 0);
-			status = pg_GSS_recvauth(port);
+			port->gss->auth = true;
+			if (port->gss->enc)
+				status = pg_GSS_checkauth(port);
+			else
+			{
+				sendAuthRequest(port, AUTH_REQ_GSS, NULL, 0);
+				status = pg_GSS_recvauth(port);
+			}
 #else
 			Assert(false);
 #endif
@@ -1031,68 +1046,6 @@ CheckSCRAMAuth(Port *port, char *shadow_pass, char **logdetail)
  *----------------------------------------------------------------
  */
 #ifdef ENABLE_GSS
-
-#if defined(WIN32) && !defined(_MSC_VER)
-/*
- * 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
-
-
-/*
- * Generate an error for GSSAPI authentication.  The caller should apply
- * _() to errmsg to make it translatable.
- */
-static void
-pg_GSS_error(int severity, const 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)
 {
@@ -1101,7 +1054,6 @@ pg_GSS_recvauth(Port *port)
 				lmin_s,
 				gflags;
 	int			mtype;
-	int			ret;
 	StringInfoData buf;
 	gss_buffer_desc gbuf;
 
@@ -1254,10 +1206,23 @@ pg_GSS_recvauth(Port *port)
 		 */
 		gss_release_cred(&min_stat, &port->gss->cred);
 	}
+	return pg_GSS_checkauth(port);
+}
+
+/*
+ * Check whether the GSSAPI-authenticated user is allowed to connect as the
+ * claimed username.
+ */
+static int
+pg_GSS_checkauth(Port *port)
+{
+	int			ret;
+	OM_uint32	maj_stat,
+				min_stat,
+				lmin_s;
+	gss_buffer_desc gbuf;
 
 	/*
-	 * 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.
 	 */
@@ -1267,6 +1232,12 @@ pg_GSS_recvauth(Port *port)
 					 _("retrieving GSS user name failed"),
 					 maj_stat, min_stat);
 
+	/*
+	 * Copy the original name of the authenticated principal into our backend
+	 * memory for display later.
+	 */
+	port->gss->princ = MemoryContextStrdup(TopMemoryContext, gbuf.value);
+
 	/*
 	 * Split the username at the realm separator
 	 */
diff --git a/src/backend/libpq/be-gssapi-common.c b/src/backend/libpq/be-gssapi-common.c
new file mode 100644
index 0000000000000000000000000000000000000000..40ada14bdde415b6d02c2715014f5d79b0b2e865
--- /dev/null
+++ b/src/backend/libpq/be-gssapi-common.c
@@ -0,0 +1,74 @@
+/*-------------------------------------------------------------------------
+ *
+ * be-gssapi-common.c
+ *     Common code for GSSAPI authentication and encryption
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *       src/backend/libpq/be-gssapi-common.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "be-gssapi-common.h"
+
+/*
+ * Helper function for getting all strings of a GSSAPI error (of specified
+ * stat).  Call once for GSS_CODE and once for MECH_CODE.
+ */
+static void
+pg_GSS_error_int(char *s, size_t len, OM_uint32 stat, int type)
+{
+	gss_buffer_desc gmsg;
+	size_t		i = 0;
+	OM_uint32	lmin_s,
+				msg_ctx = 0;
+
+	gmsg.value = NULL;
+	gmsg.length = 0;
+
+	do
+	{
+		gss_display_status(&lmin_s, stat, type,
+						   GSS_C_NO_OID, &msg_ctx, &gmsg);
+		strlcpy(s + i, gmsg.value, len - i);
+		i += gmsg.length;
+		gss_release_buffer(&lmin_s, &gmsg);
+	}
+	while (msg_ctx && i < len);
+
+	if (msg_ctx || i == len)
+		ereport(WARNING,
+				(errmsg_internal("incomplete GSS error report")));
+}
+
+/*
+ * Fetch and report all error messages from GSSAPI.  To avoid allocation,
+ * total error size is capped (at 128 bytes for each of major and minor).  No
+ * known mechanisms will produce error messages beyond this cap.
+ */
+void
+pg_GSS_error(int severity, const char *errmsg,
+			 OM_uint32 maj_stat, OM_uint32 min_stat)
+{
+	char		msg_major[128],
+				msg_minor[128];
+
+	/* Fetch major status message */
+	pg_GSS_error_int(msg_major, sizeof(msg_major), maj_stat, GSS_C_GSS_CODE);
+
+	/* Fetch mechanism minor status message */
+	pg_GSS_error_int(msg_minor, sizeof(msg_minor), min_stat, GSS_C_MECH_CODE);
+
+	/*
+	 * 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)));
+}
diff --git a/src/backend/libpq/be-gssapi-common.h b/src/backend/libpq/be-gssapi-common.h
new file mode 100644
index 0000000000000000000000000000000000000000..f6e90ea3a7c476958e1ee88a73e352749c3441cc
--- /dev/null
+++ b/src/backend/libpq/be-gssapi-common.h
@@ -0,0 +1,26 @@
+/*-------------------------------------------------------------------------
+ *
+ * be-gssapi-common.h
+ *       Definitions for GSSAPI authentication and encryption handling
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/libpq/be-gssapi-common.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef BE_GSSAPI_COMMON_H
+#define BE_GSSAPI_COMMON_H
+
+#if defined(HAVE_GSSAPI_H)
+#include <gssapi.h>
+#else
+#include <gssapi/gssapi.h>
+#endif
+
+void pg_GSS_error(int severity, const char *errmsg,
+			 OM_uint32 maj_stat, OM_uint32 min_stat);
+
+#endif							/* BE_GSSAPI_COMMON_H */
diff --git a/src/backend/libpq/be-secure-gssapi.c b/src/backend/libpq/be-secure-gssapi.c
new file mode 100644
index 0000000000000000000000000000000000000000..6089d627abbd97f45411774095559e4aaeaab285
--- /dev/null
+++ b/src/backend/libpq/be-secure-gssapi.c
@@ -0,0 +1,627 @@
+/*-------------------------------------------------------------------------
+ *
+ * be-secure-gssapi.c
+ *  GSSAPI encryption support
+ *
+ * Portions Copyright (c) 2018-2018, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ *  src/backend/libpq/be-secure-gssapi.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "be-gssapi-common.h"
+
+#include "libpq/auth.h"
+#include "libpq/libpq.h"
+#include "libpq/libpq-be.h"
+#include "libpq/pqformat.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+
+#include <unistd.h>
+
+
+/*
+ * Handle the encryption/decryption of data using GSSAPI.
+ *
+ * In the encrypted data stream on the wire, we break up the data
+ * into packets where each packet starts with a sizeof(uint32)-byte
+ * length (not allowed to be larger than the buffer sizes defined
+ * below) and then the encrypted data of that length immediately
+ * following.
+ *
+ * Encrypted data typically ends up being larger than the same data
+ * unencrypted, so we use fixed-size buffers for handling the
+ * encryption/decryption which are larger than PQComm's buffer will
+ * typically be to minimize the times where we have to make multiple
+ * packets and therefore sets of recv/send calls for a single
+ * read/write call to us.
+ *
+ * NOTE: The client and server have to agree on the max packet size,
+ * because we have to pass an entire packet to GSSAPI at a time and we
+ * don't want the other side to send arbitrairly huge packets as we
+ * would have to allocate memory for them to then pass them to GSSAPI.
+ */
+#define PQ_GSS_SEND_BUFFER_SIZE 16384
+#define PQ_GSS_RECV_BUFFER_SIZE 16384
+
+/* PqGSSSendBuffer is for *encrypted* data */
+static char PqGSSSendBuffer[PQ_GSS_SEND_BUFFER_SIZE];
+static int	PqGSSSendPointer;	/* Next index to store a byte in
+								 * PqGSSSendBuffer */
+static int	PqGSSSendStart;		/* Next index to send a byte in
+								 * PqGSSSendBuffer */
+
+/* PqGSSRecvBuffer is for *encrypted* data */
+static char PqGSSRecvBuffer[PQ_GSS_RECV_BUFFER_SIZE];
+static int	PqGSSRecvLength;	/* End of data available in PqGSSRecvBuffer */
+
+/* PqGSSResultBuffer is for *unencrypted* data */
+static char PqGSSResultBuffer[PQ_GSS_RECV_BUFFER_SIZE];
+static int	PqGSSResultPointer; /* Next index to read a byte from
+								 * PqGSSResultBuffer */
+static int	PqGSSResultLength;	/* End of data available in PqGSSResultBuffer */
+
+uint32		max_packet_size;	/* Maximum size we can encrypt and fit the
+								 * results into our output buffer */
+
+/*
+ * Attempt to write len bytes of data from ptr along a GSSAPI-encrypted connection.
+ *
+ * Connection must be fully established (including authentication step) before
+ * calling.  Returns the bytes actually consumed once complete.  Data is
+ * internally buffered; in the case of an incomplete write, the amount of data we
+ * processed (encrypted into our output buffer to be sent) will be returned.  If
+ * an error occurs or we would block, a negative value is returned and errno is
+ * set appropriately.
+ *
+ * To continue writing in the case of EWOULDBLOCK and similar, call this function
+ * again with matching ptr and len parameters.
+ */
+ssize_t
+be_gssapi_write(Port *port, void *ptr, size_t len)
+{
+	size_t		bytes_to_encrypt = len;
+	size_t		bytes_encrypted = 0;
+
+	/*
+	 * Loop through encrypting data and sending it out until
+	 * secure_raw_write() complains (which would likely mean that the socket
+	 * is non-blocking and the requested send() would block, or there was some
+	 * kind of actual error) and then return.
+	 */
+	while (bytes_to_encrypt || PqGSSSendPointer)
+	{
+		OM_uint32	major,
+					minor;
+		gss_buffer_desc input,
+					output;
+		int			conf = 0;
+		uint32		netlen;
+		pg_gssinfo *gss = port->gss;
+
+		/*
+		 * Check if we have data in the encrypted output buffer that needs to
+		 * be sent, and if so, try to send it.  If we aren't able to, return
+		 * that back up to the caller.
+		 */
+		if (PqGSSSendPointer)
+		{
+			ssize_t		ret;
+			ssize_t		amount = PqGSSSendPointer - PqGSSSendStart;
+
+			ret = secure_raw_write(port, PqGSSSendBuffer + PqGSSSendStart, amount);
+			if (ret <= 0)
+			{
+				/*
+				 * If we encrypted some data and it's in our output buffer,
+				 * but send() is saying that we would block, then tell the
+				 * caller how far we got with encrypting the data so that they
+				 * can call us again with whatever is left, at which point we
+				 * will try to send the remaining encrypted data first and
+				 * then move on to encrypting the rest of the data.
+				 */
+				if (bytes_encrypted != 0 && (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR))
+					return bytes_encrypted;
+				else
+					return ret;
+			}
+
+			/*
+			 * Check if this was a partial write, and if so, move forward that
+			 * far in our buffer and try again.
+			 */
+			if (ret != amount)
+			{
+				PqGSSSendStart += ret;
+				continue;
+			}
+
+			/* All encrypted data was sent, our buffer is empty now. */
+			PqGSSSendPointer = PqGSSSendStart = 0;
+		}
+
+		/*
+		 * Check if there are any bytes left to encrypt.  If not, we're done.
+		 */
+		if (!bytes_to_encrypt)
+			return bytes_encrypted;
+
+		/*
+		 * max_packet_size is the maximum amount of unencrypted data that,
+		 * when encrypted, will fit into our encrypted-data output buffer.
+		 *
+		 * If we are being asked to send more than max_packet_size unencrypted
+		 * data, then we will loop and create multiple packets, each with
+		 * max_packet_size unencrypted data encrypted in them (at least, until
+		 * secure_raw_write returns a failure saying we would be blocked, at
+		 * which point we will let the caller know how far we got).
+		 */
+		if (bytes_to_encrypt > max_packet_size)
+			input.length = max_packet_size;
+		else
+			input.length = bytes_to_encrypt;
+
+		input.value = (char *) ptr + bytes_encrypted;
+
+		output.value = NULL;
+		output.length = 0;
+
+		/* Create the next encrypted packet */
+		major = gss_wrap(&minor, gss->ctx, 1, GSS_C_QOP_DEFAULT,
+						 &input, &conf, &output);
+		if (major != GSS_S_COMPLETE)
+			pg_GSS_error(FATAL, gettext_noop("GSSAPI wrap error"), major, minor);
+
+		if (conf == 0)
+			ereport(FATAL, (errmsg("GSSAPI did not provide confidentiality")));
+
+		if (output.length > PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))
+			ereport(FATAL, (errmsg("GSSAPI tried to send packet of size: %ld", output.length)));
+
+		bytes_encrypted += input.length;
+		bytes_to_encrypt -= input.length;
+
+		/* 4 network-order length bytes, then payload */
+		netlen = htonl(output.length);
+		memcpy(PqGSSSendBuffer + PqGSSSendPointer, &netlen, sizeof(uint32));
+		PqGSSSendPointer += sizeof(uint32);
+
+		memcpy(PqGSSSendBuffer + PqGSSSendPointer, output.value, output.length);
+		PqGSSSendPointer += output.length;
+	}
+
+	return bytes_encrypted;
+}
+
+/*
+ * Read up to len bytes from a GSSAPI-encrypted connection into ptr.  Call
+ * only after the connection has been fully established (i.e., GSSAPI
+ * authentication is complete).  On success, returns the number of bytes
+ * written into ptr; otherwise, returns -1 and sets errno appropriately.
+ */
+ssize_t
+be_gssapi_read(Port *port, void *ptr, size_t len)
+{
+	OM_uint32	major,
+				minor;
+	gss_buffer_desc input,
+				output;
+	ssize_t		ret;
+	size_t		bytes_to_return = len;
+	size_t		bytes_returned = 0;
+	int			conf = 0;
+	pg_gssinfo *gss = port->gss;
+
+	/*
+	 * The goal here is to read an incoming encrypted packet, one at a time,
+	 * decrypt it into our out buffer, returning to the caller what they asked
+	 * for, and then saving anything else for the next call.
+	 *
+	 * First we look to see if we have unencrypted bytes available and, if so,
+	 * copy those to the result.  If the caller asked for more than we had
+	 * immediately available, then we try to read a packet off the wire and
+	 * decrypt it.  If the read would block, then return the amount of
+	 * unencrypted data we copied into the caller's ptr.
+	 */
+	while (bytes_to_return)
+	{
+		/* Check if we have data in our buffer that we can return immediately */
+		if (PqGSSResultPointer < PqGSSResultLength)
+		{
+			int			bytes_in_buffer = PqGSSResultLength - PqGSSResultPointer;
+			int			bytes_to_copy = bytes_in_buffer < len - bytes_returned ? bytes_in_buffer : len - bytes_returned;
+
+			/*
+			 * Copy the data from our output buffer into the caller's buffer,
+			 * at the point where we last left off filling their buffer
+			 */
+			memcpy((char *) ptr + bytes_returned, PqGSSResultBuffer + PqGSSResultPointer, bytes_to_copy);
+			PqGSSResultPointer += bytes_to_copy;
+			bytes_to_return -= bytes_to_copy;
+			bytes_returned += bytes_to_copy;
+
+			/* Check if our result buffer is now empty and, if so, reset */
+			if (PqGSSResultPointer == PqGSSResultLength)
+				PqGSSResultPointer = PqGSSResultLength = 0;
+
+			continue;
+		}
+
+		/*
+		 * At this point, our output buffer should be empty with more bytes
+		 * being requested to be read.  We are now ready to load the next
+		 * packet and decrypt it (entirely) into our buffer.
+		 *
+		 * If we get a partial read back while trying to read a packet off the
+		 * wire then we return the number of unencrypted bytes we were able to
+		 * copy (if any, if we didn't copy any, then we return whatever
+		 * secure_raw_read returned when we called it; likely -1) into the
+		 * caller's ptr and wait to be called again, until we get a full
+		 * packet to decrypt.
+		 */
+
+		/* Check if we have the size of the packet already in our buffer. */
+		if (PqGSSRecvLength < sizeof(uint32))
+		{
+			/*
+			 * We were not able to get the length of the packet last time, so
+			 * we need to do that first.
+			 */
+			ret = secure_raw_read(port, PqGSSRecvBuffer + PqGSSRecvLength,
+								  sizeof(uint32) - PqGSSRecvLength);
+			if (ret < 0)
+				return bytes_returned ? bytes_returned : ret;
+
+			PqGSSRecvLength += ret;
+
+			/*
+			 * If we only got part of the packet length, then return however
+			 * many unencrypted bytes we copied to the caller and wait to be
+			 * called again.
+			 */
+			if (PqGSSRecvLength < sizeof(uint32))
+				return bytes_returned;
+		}
+
+		/*
+		 * We have the length of the next packet at this point, so pull it out
+		 * and then read whatever we have left of the packet to read.
+		 */
+		input.length = ntohl(*(uint32 *) PqGSSRecvBuffer);
+
+		/* Check for over-length packet */
+		if (input.length > PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32))
+			ereport(FATAL, (errmsg("Over-size GSSAPI packet sent by the client.")));
+
+		/*
+		 * Read as much of the packet as we are able to on this call into
+		 * wherever we left off from the last time we were called.
+		 */
+		ret = secure_raw_read(port, PqGSSRecvBuffer + PqGSSRecvLength,
+							  input.length - (PqGSSRecvLength - sizeof(uint32)));
+		if (ret < 0)
+			return bytes_returned ? bytes_returned : ret;
+
+		PqGSSRecvLength += ret;
+
+		/*
+		 * If we got less than the rest of the packet then we need to return
+		 * and be called again.  If we didn't have any bytes to return on this
+		 * run then return -1 and set errno to EWOULDBLOCK.
+		 */
+		if (PqGSSRecvLength - sizeof(uint32) < input.length)
+		{
+			if (!bytes_returned)
+			{
+				errno = EWOULDBLOCK;
+				return -1;
+			}
+
+			return bytes_returned;
+		}
+
+		/*
+		 * We now have the full packet and we can perform the decryption and
+		 * refill our output buffer, then loop back up to pass that back to
+		 * the user.
+		 */
+		output.value = NULL;
+		output.length = 0;
+		input.value = PqGSSRecvBuffer + sizeof(uint32);
+
+		major = gss_unwrap(&minor, gss->ctx, &input, &output, &conf, NULL);
+		if (major != GSS_S_COMPLETE)
+			pg_GSS_error(FATAL, gettext_noop("GSSAPI unwrap error"),
+						 major, minor);
+
+		if (conf == 0)
+			ereport(FATAL, (errmsg("GSSAPI did not provide confidentiality")));
+
+		memcpy(PqGSSResultBuffer, output.value, output.length);
+
+		PqGSSResultLength = output.length;
+
+		/* Our buffer is now empty, reset it */
+		PqGSSRecvLength = 0;
+
+		gss_release_buffer(&minor, &output);
+	}
+
+	return bytes_returned;
+}
+
+/*
+ * Read the specified number of bytes off the wire, waiting using
+ * WaitLatchOrSocket if we would block.
+ *
+ * Results are read into PqGSSRecvBuffer.
+ *
+ * Will always return either -1, to indicate a permanent error, or len.
+ */
+static ssize_t
+read_or_wait(Port *port, ssize_t len)
+{
+	ssize_t		ret;
+
+	/*
+	 * Keep going until we either read in everything we were asked to, or we
+	 * error out.
+	 */
+	while (PqGSSRecvLength != len)
+	{
+		ret = secure_raw_read(port, PqGSSRecvBuffer + PqGSSRecvLength, len - PqGSSRecvLength);
+
+		/*
+		 * If we got back an error and it wasn't just EWOULDBLOCK/EAGAIN, then
+		 * give up.
+		 */
+		if (ret < 0 && !(errno == EWOULDBLOCK || errno == EAGAIN))
+			return -1;
+
+		/*
+		 * Ok, we got back either a positive value, zero, or a negative result
+		 * but EWOULDBLOCK or EAGAIN was set.
+		 *
+		 * If it was zero or negative, then we try to wait on the socket to be
+		 * readable again.
+		 */
+		if (ret <= 0)
+		{
+			/*
+			 * If we got back less than zero, indicating an error, and that
+			 * wasn't just a EWOULDBOCK/EAGAIN, then give up.
+			 */
+			if (ret < 0 && !(errno == EWOULDBLOCK || errno == EAGAIN))
+				return -1;
+
+			/*
+			 * We got back either zero, or -1 with EWOULDBLOCK/EAGAIN, so wait
+			 * on socket to be readable again.
+			 */
+			WaitLatchOrSocket(MyLatch,
+							  WL_SOCKET_READABLE | WL_EXIT_ON_PM_DEATH,
+							  port->sock, 0, WAIT_EVENT_GSS_OPEN_SERVER);
+
+			/*
+			 * If we got back zero bytes, and then waited on the socket to be
+			 * readable and got back zero bytes on a second read, then this is
+			 * EOF and the client hung up on us.
+			 *
+			 * If we did get data here, then we can just fall through and
+			 * handle it just as if we got data the first time.
+			 *
+			 * Otherwise loop back to the top and try again.
+			 */
+			if (ret == 0)
+			{
+				ret = secure_raw_read(port, PqGSSRecvBuffer + PqGSSRecvLength, len - PqGSSRecvLength);
+				if (ret == 0)
+					return -1;
+			}
+			else
+				continue;
+		}
+
+		PqGSSRecvLength += ret;
+	}
+
+	return len;
+}
+
+/*
+ * Start up a GSSAPI-encrypted connection.  This performs GSSAPI
+ * authentication; after this function completes, it is safe to call
+ * be_gssapi_read and be_gssapi_write.  Returns -1 and logs on failure;
+ * otherwise, returns 0 and marks the connection as ready for GSSAPI
+ * encryption.
+ *
+ * Note that unlike the be_gssapi_read/be_gssapi_write functions, this
+ * function WILL block on the socket to be ready for read/write (using
+ * WaitLatchOrSocket) as appropriate while establishing the GSSAPI
+ * session.
+ */
+ssize_t
+secure_open_gssapi(Port *port)
+{
+	bool		complete_next = false;
+	OM_uint32	major,
+				minor;
+
+	/* initialize state variables */
+	PqGSSSendPointer = PqGSSSendStart = PqGSSRecvLength = PqGSSResultPointer = PqGSSResultLength = 0;
+
+	/*
+	 * Use the configured keytab, if there is one.  Unfortunately, Heimdal
+	 * doesn't support the cred store extensions, so use the env var.
+	 */
+	if (pg_krb_server_keyfile != NULL && strlen(pg_krb_server_keyfile) > 0)
+		setenv("KRB5_KTNAME", pg_krb_server_keyfile, 1);
+
+	while (true)
+	{
+		ssize_t		ret;
+		gss_buffer_desc input,
+					output = GSS_C_EMPTY_BUFFER;
+
+		/*
+		 * The client always sends first, so try to go ahead and read the
+		 * length and wait on the socket to be readable again if that fails.
+		 */
+		ret = read_or_wait(port, sizeof(uint32));
+		if (ret < 0)
+			return ret;
+
+		/*
+		 * Get the length for this packet from the length header.
+		 */
+		input.length = ntohl(*(uint32 *) PqGSSRecvBuffer);
+
+		/* Done with the length, reset our buffer */
+		PqGSSRecvLength = 0;
+
+		/*
+		 * During initialization, packets are always fully consumed and
+		 * shouldn't ever be over PQ_GSS_RECV_BUFFER_SIZE in length.
+		 *
+		 * Verify on our side that the client doesn't do something funny.
+		 */
+		if (input.length > PQ_GSS_RECV_BUFFER_SIZE)
+			ereport(FATAL, (errmsg("Over-size GSSAPI packet sent by the client: %ld", input.length)));
+
+		/*
+		 * Get the rest of the packet so we can pass it to GSSAPI to accept
+		 * the context.
+		 */
+		ret = read_or_wait(port, input.length);
+		if (ret < 0)
+			return ret;
+
+		input.value = PqGSSRecvBuffer;
+
+		/* Process incoming data.  (The client sends first.) */
+		major = gss_accept_sec_context(&minor, &port->gss->ctx,
+									   GSS_C_NO_CREDENTIAL, &input,
+									   GSS_C_NO_CHANNEL_BINDINGS,
+									   &port->gss->name, NULL, &output, NULL,
+									   NULL, NULL);
+		if (GSS_ERROR(major))
+		{
+			pg_GSS_error(ERROR, gettext_noop("GSSAPI context error"),
+						 major, minor);
+			gss_release_buffer(&minor, &output);
+			return -1;
+		}
+		else if (!(major & GSS_S_CONTINUE_NEEDED))
+		{
+			/*
+			 * rfc2744 technically permits context negotiation to be complete
+			 * both with and without a packet to be sent.
+			 */
+			complete_next = true;
+		}
+
+		/* Done handling the incoming packet, reset our buffer */
+		PqGSSRecvLength = 0;
+
+		/*
+		 * Check if we have data to send and, if we do, make sure to send it
+		 * all
+		 */
+		if (output.length != 0)
+		{
+			uint32		netlen = htonl(output.length);
+
+			if (output.length > PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))
+				ereport(FATAL, (errmsg("GSSAPI tried to send oversize packet")));
+
+			memcpy(PqGSSSendBuffer, (char *) &netlen, sizeof(uint32));
+			PqGSSSendPointer += sizeof(uint32);
+
+			memcpy(PqGSSSendBuffer + PqGSSSendPointer, output.value, output.length);
+			PqGSSSendPointer += output.length;
+
+			while (PqGSSSendStart != sizeof(uint32) + output.length)
+			{
+				ret = secure_raw_write(port, PqGSSSendBuffer + PqGSSSendStart, sizeof(uint32) + output.length - PqGSSSendStart);
+				if (ret <= 0)
+				{
+					WaitLatchOrSocket(MyLatch,
+									  WL_SOCKET_WRITEABLE | WL_EXIT_ON_PM_DEATH,
+									  port->sock, 0, WAIT_EVENT_GSS_OPEN_SERVER);
+					continue;
+				}
+
+				PqGSSSendStart += ret;
+			}
+
+			/* Done sending the packet, reset our buffer */
+			PqGSSSendStart = PqGSSSendPointer = 0;
+
+			gss_release_buffer(&minor, &output);
+		}
+
+		/*
+		 * If we got back that the connection is finished being set up, now
+		 * that's we've sent the last packet, exit our loop.
+		 */
+		if (complete_next)
+			break;
+	}
+
+	/*
+	 * Determine the max packet size which will fit in our buffer, after
+	 * accounting for the length
+	 */
+	major = gss_wrap_size_limit(&minor, port->gss->ctx, 1, GSS_C_QOP_DEFAULT,
+								PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32), &max_packet_size);
+
+	if (GSS_ERROR(major))
+		pg_GSS_error(FATAL, gettext_noop("GSSAPI size check error"),
+					 major, minor);
+
+	port->gss->enc = true;
+
+	return 0;
+}
+
+/*
+ * Return if GSSAPI authentication was used on this connection.
+ */
+bool
+be_gssapi_get_auth(Port *port)
+{
+	if (!port || !port->gss)
+		return false;
+
+	return port->gss->auth;
+}
+
+/*
+ * Return if GSSAPI encryption is enabled and being used on this connection.
+ */
+bool
+be_gssapi_get_enc(Port *port)
+{
+	if (!port || !port->gss)
+		return false;
+
+	return port->gss->enc;
+}
+
+/*
+ * Return the GSSAPI principal used for authentication on this connection.
+ */
+const char *
+be_gssapi_get_princ(Port *port)
+{
+	if (!port || !port->gss->auth)
+		return NULL;
+
+	return port->gss->princ;
+}
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index a7def3168d1285dc93fce68b121bfe4963edf44e..b90eb0ab6b7ec210013820aa2245ad8b1ba8e281 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -159,6 +159,14 @@ retry:
 		n = be_tls_read(port, ptr, len, &waitfor);
 	}
 	else
+#endif
+#ifdef ENABLE_GSS
+	if (port->gss->enc)
+	{
+		n = be_gssapi_read(port, ptr, len);
+		waitfor = WL_SOCKET_READABLE;
+	}
+	else
 #endif
 	{
 		n = secure_raw_read(port, ptr, len);
@@ -264,6 +272,14 @@ retry:
 		n = be_tls_write(port, ptr, len, &waitfor);
 	}
 	else
+#endif
+#ifdef ENABLE_GSS
+	if (port->gss->enc)
+	{
+		n = be_gssapi_write(port, ptr, len);
+		waitfor = WL_SOCKET_WRITEABLE;
+	}
+	else
 #endif
 	{
 		n = secure_raw_write(port, ptr, len);
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index ce9bca868ccf0d7b545b6494c097e203ddc81532..37d5ad44a54e1c59bb060c19d5c5bf8240c8d46b 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -994,7 +994,9 @@ parse_hba_line(TokenizedLine *tok_line, int elevel)
 	}
 	else if (strcmp(token->string, "host") == 0 ||
 			 strcmp(token->string, "hostssl") == 0 ||
-			 strcmp(token->string, "hostnossl") == 0)
+			 strcmp(token->string, "hostnossl") == 0 ||
+			 strcmp(token->string, "hostgssenc") == 0 ||
+			 strcmp(token->string, "hostnogssenc") == 0)
 	{
 
 		if (token->string[4] == 's')	/* "hostssl" */
@@ -1022,10 +1024,23 @@ parse_hba_line(TokenizedLine *tok_line, int elevel)
 			*err_msg = "hostssl record cannot match because SSL is not supported by this build";
 #endif
 		}
-		else if (token->string[4] == 'n')	/* "hostnossl" */
+		else if (token->string[4] == 'g')	/* "hostgssenc" */
 		{
-			parsedline->conntype = ctHostNoSSL;
+			parsedline->conntype = ctHostGSS;
+#ifndef ENABLE_GSS
+			ereport(elevel,
+					(errcode(ERRCODE_CONFIG_FILE_ERROR),
+					 errmsg("hostgssenc record cannot match because GSSAPI is not supported by this build"),
+					 errhint("Compile with --with-gssapi to use GSSAPI connections."),
+					 errcontext("line %d of configuration file \"%s\"",
+								line_num, HbaFileName)));
+			*err_msg = "hostgssenc record cannot match because GSSAPI is not supported by this build";
+#endif
 		}
+		else if (token->string[4] == 'n' && token->string[6] == 's')
+			parsedline->conntype = ctHostNoSSL;
+		else if (token->string[4] == 'n' && token->string[6] == 'g')
+			parsedline->conntype = ctHostNoGSS;
 		else
 		{
 			/* "host" */
@@ -1404,6 +1419,19 @@ parse_hba_line(TokenizedLine *tok_line, int elevel)
 		*err_msg = "gssapi authentication is not supported on local sockets";
 		return NULL;
 	}
+	if (parsedline->conntype == ctHostGSS &&
+		parsedline->auth_method != uaGSS &&
+		parsedline->auth_method != uaReject &&
+		parsedline->auth_method != uaTrust)
+	{
+		ereport(elevel,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("GSSAPI encryption only supports gss, trust, or reject authentication"),
+				 errcontext("line %d of configuration file \"%s\"",
+							line_num, HbaFileName)));
+		*err_msg = "GSSAPI encryption only supports gss, trust, or reject authenticaion";
+		return NULL;
+	}
 
 	if (parsedline->conntype != ctLocal &&
 		parsedline->auth_method == uaPeer)
@@ -2078,6 +2106,17 @@ check_hba(hbaPort *port)
 					continue;
 			}
 
+			/* Check GSSAPI state */
+#ifdef ENABLE_GSS
+			if (port->gss->enc && hba->conntype == ctHostNoGSS)
+				continue;
+			else if (!port->gss->enc && hba->conntype == ctHostGSS)
+				continue;
+#else
+			if (hba->conntype == ctHostGSS)
+				continue;
+#endif
+
 			/* Check IP address */
 			switch (hba->ip_cmp_method)
 			{
@@ -2414,6 +2453,12 @@ fill_hba_line(Tuplestorestate *tuple_store, TupleDesc tupdesc,
 			case ctHostNoSSL:
 				typestr = "hostnossl";
 				break;
+			case ctHostGSS:
+				typestr = "hostgssenc";
+				break;
+			case ctHostNoGSS:
+				typestr = "hostnogssenc";
+				break;
 		}
 		if (typestr)
 			values[index++] = CStringGetTextDatum(typestr);
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 2a8472b91aee2453a8c34ac916ed93743dcbf62a..0355fa65fb899b6cb6823fae5d971042e1db033a 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -2634,6 +2634,9 @@ static Size BackendActivityBufferSize = 0;
 #ifdef USE_SSL
 static PgBackendSSLStatus *BackendSslStatusBuffer = NULL;
 #endif
+#ifdef ENABLE_GSS
+static PgBackendGSSStatus *BackendGssStatusBuffer = NULL;
+#endif
 
 
 /*
@@ -2766,6 +2769,28 @@ CreateSharedBackendStatus(void)
 		}
 	}
 #endif
+
+#ifdef ENABLE_GSS
+	/* Create or attach to the shared GSSAPI status buffer */
+	size = mul_size(sizeof(PgBackendGSSStatus), NumBackendStatSlots);
+	BackendGssStatusBuffer = (PgBackendGSSStatus *)
+		ShmemInitStruct("Backend GSS Status Buffer", size, &found);
+
+	if (!found)
+	{
+		PgBackendGSSStatus *ptr;
+
+		MemSet(BackendGssStatusBuffer, 0, size);
+
+		/* Initialize st_gssstatus pointers. */
+		ptr = BackendGssStatusBuffer;
+		for (i = 0; i < NumBackendStatSlots; i++)
+		{
+			BackendStatusArray[i].st_gssstatus = ptr;
+			ptr++;
+		}
+	}
+#endif
 }
 
 
@@ -2953,6 +2978,24 @@ pgstat_bestart(void)
 #else
 	beentry->st_ssl = false;
 #endif
+
+#ifdef ENABLE_GSS
+	if (MyProcPort && MyProcPort->gss != NULL)
+	{
+		beentry->st_gss = true;
+		beentry->st_gssstatus->gss_auth = be_gssapi_get_auth(MyProcPort);
+		beentry->st_gssstatus->gss_enc = be_gssapi_get_enc(MyProcPort);
+
+		if (beentry->st_gssstatus->gss_auth)
+			strlcpy(beentry->st_gssstatus->gss_princ, be_gssapi_get_princ(MyProcPort), NAMEDATALEN);
+	}
+	else
+	{
+		beentry->st_gss = false;
+	}
+#else
+	beentry->st_gss = false;
+#endif
 	beentry->st_state = STATE_UNDEFINED;
 	beentry->st_appname[0] = '\0';
 	beentry->st_activity_raw[0] = '\0';
@@ -3595,6 +3638,9 @@ pgstat_get_wait_client(WaitEventClient w)
 		case WAIT_EVENT_WAL_SENDER_WRITE_DATA:
 			event_name = "WalSenderWriteData";
 			break;
+		case WAIT_EVENT_GSS_OPEN_SERVER:
+			event_name = "GSSOpenServer";
+			break;
 			/* no default case, so that compiler will warn */
 	}
 
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index fe599632d3d478554a4e51b9189dac673433f4b5..067487fdcb0b2f06f796b750c69b930ca9af40d7 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -1889,9 +1889,12 @@ initMasks(fd_set *rmask)
  * if that's what you want.  Return STATUS_ERROR if you don't want to
  * send anything to the client, which would typically be appropriate
  * if we detect a communications failure.)
+ *
+ * Set secure_done when negotiation of an encrypted layer (currently, TLS or
+ * GSSAPI) is already completed.
  */
 static int
-ProcessStartupPacket(Port *port, bool SSLdone)
+ProcessStartupPacket(Port *port, bool secure_done)
 {
 	int32		len;
 	void	   *buf;
@@ -1924,9 +1927,10 @@ ProcessStartupPacket(Port *port, bool SSLdone)
 	if (pq_getbytes(((char *) &len) + 1, 3) == EOF)
 	{
 		/* Got a partial length word, so bleat about that */
-		ereport(COMMERROR,
-				(errcode(ERRCODE_PROTOCOL_VIOLATION),
-				 errmsg("incomplete startup packet")));
+		if (!secure_done)
+			ereport(COMMERROR,
+					(errcode(ERRCODE_PROTOCOL_VIOLATION),
+					 errmsg("incomplete startup packet")));
 		return STATUS_ERROR;
 	}
 
@@ -1975,7 +1979,7 @@ ProcessStartupPacket(Port *port, bool SSLdone)
 		return STATUS_ERROR;
 	}
 
-	if (proto == NEGOTIATE_SSL_CODE && !SSLdone)
+	if (proto == NEGOTIATE_SSL_CODE && !secure_done)
 	{
 		char		SSLok;
 
@@ -2008,6 +2012,32 @@ retry1:
 		/* but not another SSL negotiation request */
 		return ProcessStartupPacket(port, true);
 	}
+	else if (proto == NEGOTIATE_GSS_CODE && !secure_done)
+	{
+		char		GSSok = 'N';
+#ifdef ENABLE_GSS
+		/* No GSSAPI encryption when on Unix socket */
+		if (!IS_AF_UNIX(port->laddr.addr.ss_family))
+			GSSok = 'G';
+#endif
+
+		while (send(port->sock, &GSSok, 1, 0) != 1)
+		{
+			if (errno == EINTR)
+				continue;
+			ereport(COMMERROR,
+					(errcode_for_socket_access(),
+					 errmsg("failed to send GSSAPI negotiation response: %m)")));
+			return STATUS_ERROR;	/* close the connection */
+		}
+
+#ifdef ENABLE_GSS
+		if (GSSok == 'G' && secure_open_gssapi(port) == -1)
+			return STATUS_ERROR;
+#endif
+		/* Won't ever see more than one negotiation request */
+		return ProcessStartupPacket(port, true);
+	}
 
 	/* Could add additional special packet types here */
 
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 7c2afe64272244c80cfb57b6b81e7a0b239258bb..9a1d07bee3304c9f80abdeb31379ba40005a2842 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -545,7 +545,7 @@ pg_stat_get_progress_info(PG_FUNCTION_ARGS)
 Datum
 pg_stat_get_activity(PG_FUNCTION_ARGS)
 {
-#define PG_STAT_GET_ACTIVITY_COLS	26
+#define PG_STAT_GET_ACTIVITY_COLS	29
 	int			num_backends = pgstat_fetch_stat_numbackends();
 	int			curr_backend;
 	int			pid = PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0);
@@ -859,6 +859,21 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 				values[18] = BoolGetDatum(false);	/* ssl */
 				nulls[19] = nulls[20] = nulls[21] = nulls[22] = nulls[23] = nulls[24] = nulls[25] = true;
 			}
+
+			/* GSSAPI information */
+			if (beentry->st_gss)
+			{
+				values[26] = BoolGetDatum(beentry->st_gssstatus->gss_auth); /* gss_auth */
+				values[27] = CStringGetTextDatum(beentry->st_gssstatus->gss_princ);
+				values[28] = BoolGetDatum(beentry->st_gssstatus->gss_enc);	/* GSS Encryption in use */
+			}
+			else
+			{
+				values[26] = BoolGetDatum(false);	/* gss_auth */
+				nulls[27] = true;	/* No GSS principal */
+				values[28] = BoolGetDatum(false);	/* GSS Encryption not in
+													 * use */
+			}
 		}
 		else
 		{
@@ -883,6 +898,9 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
 			nulls[23] = true;
 			nulls[24] = true;
 			nulls[25] = true;
+			nulls[26] = true;
+			nulls[27] = true;
+			nulls[28] = true;
 		}
 
 		tuplestore_putvalues(tupstore, tupdesc, values, nulls);
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 72188b7f3ef34f8e0c52397112d47cc658fae545..bc3d10e5158404fffa0b6f3f2ee62a4b9c23ea58 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -160,6 +160,7 @@ static void print_with_linenumbers(FILE *output, char *lines,
 static void minimal_error_message(PGresult *res);
 
 static void printSSLInfo(void);
+static void printGSSInfo(void);
 static bool printPsetInfo(const char *param, struct printQueryOpt *popt);
 static char *pset_value_string(const char *param, struct printQueryOpt *popt);
 
@@ -621,6 +622,7 @@ exec_command_conninfo(PsqlScanState scan_state, bool active_branch)
 						   db, PQuser(pset.db), host, PQport(pset.db));
 			}
 			printSSLInfo();
+			printGSSInfo();
 		}
 	}
 
@@ -3184,6 +3186,7 @@ connection_warnings(bool in_startup)
 		checkWin32Codepage();
 #endif
 		printSSLInfo();
+		printGSSInfo();
 	}
 }
 
@@ -3216,6 +3219,20 @@ printSSLInfo(void)
 		   (compression && strcmp(compression, "off") != 0) ? _("on") : _("off"));
 }
 
+/*
+ * printGSSInfo
+ *
+ * Prints information about the current GSSAPI connection, if GSSAPI encryption is in use
+ */
+static void
+printGSSInfo(void)
+{
+	if (!PQgssEncInUse(pset.db))
+		return;					/* no GSSAPI encryption in use */
+
+	printf(_("GSSAPI Encrypted connection\n"));
+}
+
 
 /*
  * checkWin32Codepage
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index a7050edca09b642b893a4c3c24ef74d8bb244d5d..fb257c17c8924935af2dfed400839997eb23d780 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5117,9 +5117,9 @@
   proname => 'pg_stat_get_activity', prorows => '100', proisstrict => 'f',
   proretset => 't', provolatile => 's', proparallel => 'r',
   prorettype => 'record', proargtypes => 'int4',
-  proallargtypes => '{int4,oid,int4,oid,text,text,text,text,text,timestamptz,timestamptz,timestamptz,timestamptz,inet,text,int4,xid,xid,text,bool,text,text,int4,bool,text,numeric,text}',
-  proargmodes => '{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}',
-  proargnames => '{pid,datid,pid,usesysid,application_name,state,query,wait_event_type,wait_event,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin,backend_type,ssl,sslversion,sslcipher,sslbits,sslcompression,ssl_client_dn,ssl_client_serial,ssl_issuer_dn}',
+  proallargtypes => '{int4,oid,int4,oid,text,text,text,text,text,timestamptz,timestamptz,timestamptz,timestamptz,inet,text,int4,xid,xid,text,bool,text,text,int4,bool,text,numeric,text,bool,text,bool}',
+  proargmodes => '{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}',
+  proargnames => '{pid,datid,pid,usesysid,application_name,state,query,wait_event_type,wait_event,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin,backend_type,ssl,sslversion,sslcipher,sslbits,sslcompression,ssl_client_dn,ssl_client_serial,ssl_issuer_dn,gss_auth,gss_princ,gss_enc}',
   prosrc => 'pg_stat_get_activity' },
 { oid => '3318',
   descr => 'statistics: information about progress of backends running maintenance command',
diff --git a/src/include/libpq/hba.h b/src/include/libpq/hba.h
index c65eb9dc8a5a57f19472fc4012aad157333abb5c..186e433574818a0eef72cd922c4d9ff2d7e17721 100644
--- a/src/include/libpq/hba.h
+++ b/src/include/libpq/hba.h
@@ -55,7 +55,9 @@ typedef enum ConnType
 	ctLocal,
 	ctHost,
 	ctHostSSL,
-	ctHostNoSSL
+	ctHostNoSSL,
+	ctHostGSS,
+	ctHostNoGSS,
 } ConnType;
 
 typedef enum ClientCertMode
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 248055f10b4eb986907888961d03bc91b01a1ec6..059218c85a3df31ed5de0334ec66fa6b9847742e 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -86,6 +86,10 @@ 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 */
+	char	   *princ;			/* GSSAPI Principal used for auth, NULL if
+								 * GSSAPI auth was not used */
+	bool		auth;			/* GSSAPI Authentication used */
+	bool		enc;			/* GSSAPI encryption in use */
 #endif
 } pg_gssinfo;
 #endif
@@ -164,6 +168,9 @@ typedef struct Port
 	int			keepalives_interval;
 	int			keepalives_count;
 
+	/*
+	 * GSSAPI structures.
+	 */
 #if defined(ENABLE_GSS) || defined(ENABLE_SSPI)
 
 	/*
@@ -262,6 +269,13 @@ extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);
 
+/*
+ * Return information about the GSSAPI authenticated connection
+ */
+extern bool be_gssapi_get_auth(Port *port);
+extern bool be_gssapi_get_enc(Port *port);
+extern const char *be_gssapi_get_princ(Port *port);
+
 /*
  * Get the server certificate hash for SCRAM channel binding type
  * tls-server-end-point.
@@ -279,6 +293,12 @@ extern char *be_tls_get_certificate_hash(Port *port, size_t *len);
 
 #endif	/* USE_SSL */
 
+#ifdef ENABLE_GSS
+/* Read and write to a GSSAPI-encrypted connection. */
+extern ssize_t be_gssapi_read(Port *port, void *ptr, size_t len);
+extern ssize_t be_gssapi_write(Port *port, void *ptr, size_t len);
+#endif							/* ENABLE_GSS */
+
 extern ProtocolVersion FrontendProtocol;
 
 /* TCP keepalives configuration. These are no-ops on an AF_UNIX socket. */
diff --git a/src/include/libpq/libpq.h b/src/include/libpq/libpq.h
index 755819cc5846300c89aa5921cbd47bbc38b21087..41f9257aa9d9d7fcaffa09780ea98c823f391696 100644
--- a/src/include/libpq/libpq.h
+++ b/src/include/libpq/libpq.h
@@ -93,6 +93,9 @@ extern ssize_t secure_read(Port *port, void *ptr, size_t len);
 extern ssize_t secure_write(Port *port, void *ptr, size_t len);
 extern ssize_t secure_raw_read(Port *port, void *ptr, size_t len);
 extern ssize_t secure_raw_write(Port *port, const void *ptr, size_t len);
+#ifdef ENABLE_GSS
+extern ssize_t secure_open_gssapi(Port *port);
+#endif
 
 extern bool ssl_loaded_verify_locations;
 
diff --git a/src/include/libpq/pqcomm.h b/src/include/libpq/pqcomm.h
index 5b84bdda99117ac30ebeb181acd4fb0a84efdd7f..baf6a4b6c029ab27ca5265fd0fb99ffc9a35ff50 100644
--- a/src/include/libpq/pqcomm.h
+++ b/src/include/libpq/pqcomm.h
@@ -199,9 +199,10 @@ typedef struct CancelRequestPacket
 
 
 /*
- * A client can also start by sending a SSL negotiation request, to get a
- * secure channel.
+ * A client can also start by sending a SSL or GSSAPI negotiation request to
+ * get a secure channel.
  */
 #define NEGOTIATE_SSL_CODE PG_PROTOCOL(1234,5679)
+#define NEGOTIATE_GSS_CODE PG_PROTOCOL(1234,5680)
 
 #endif							/* PQCOMM_H */
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index 53d4a9c43194064a4b3de89074d5e4145a2c5942..5888242f757dec70fff74bcde90ed1f5198be42b 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -801,7 +801,8 @@ typedef enum
 	WAIT_EVENT_SSL_OPEN_SERVER,
 	WAIT_EVENT_WAL_RECEIVER_WAIT_START,
 	WAIT_EVENT_WAL_SENDER_WAIT_WAL,
-	WAIT_EVENT_WAL_SENDER_WRITE_DATA
+	WAIT_EVENT_WAL_SENDER_WRITE_DATA,
+	WAIT_EVENT_GSS_OPEN_SERVER,
 } WaitEventClient;
 
 /* ----------
@@ -989,6 +990,23 @@ typedef struct PgBackendSSLStatus
 	char		ssl_issuer_dn[NAMEDATALEN];
 } PgBackendSSLStatus;
 
+/*
+ * PgBackendGSSStatus
+ *
+ * For each backend, we keep the GSS status in a separate struct, that
+ * is only filled in if GSS is enabled.
+ *
+ * All char arrays must be null-terminated.
+ */
+typedef struct PgBackendGSSStatus
+{
+	/* Information about GSSAPI connection */
+	char		gss_princ[NAMEDATALEN]; /* GSSAPI Principal used to auth */
+	bool		gss_auth;		/* If GSSAPI authentication was used */
+	bool		gss_enc;		/* If encryption is being used */
+
+} PgBackendGSSStatus;
+
 
 /* ----------
  * PgBackendStatus
@@ -1043,6 +1061,10 @@ typedef struct PgBackendStatus
 	bool		st_ssl;
 	PgBackendSSLStatus *st_sslstatus;
 
+	/* Information about GSSAPI connection */
+	bool		st_gss;
+	PgBackendGSSStatus *st_gssstatus;
+
 	/* current state */
 	BackendState st_state;
 
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index 025542dfe9fd7125148dad5ba86cee6bf9539bc2..c734965d63f86d1c74507582aa63ee6cf5fd36a4 100644
--- a/src/interfaces/libpq/Makefile
+++ b/src/interfaces/libpq/Makefile
@@ -38,6 +38,10 @@ ifeq ($(with_openssl),yes)
 OBJS += fe-secure-openssl.o fe-secure-common.o
 endif
 
+ifeq ($(with_gssapi),yes)
+OBJS += fe-gssapi-common.o fe-secure-gssapi.o
+endif
+
 ifeq ($(PORTNAME), cygwin)
 override shlib = cyg$(NAME)$(DLSUFFIX)
 endif
diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt
index cc9ee9ce6b82506ec733d66aef273176a0f9d749..7c808e5215ce8396d4b4307f2e9718081a8d3387 100644
--- a/src/interfaces/libpq/exports.txt
+++ b/src/interfaces/libpq/exports.txt
@@ -174,3 +174,5 @@ PQresultVerboseErrorMessage 171
 PQencryptPasswordConn     172
 PQresultMemorySize        173
 PQhostaddr                174
+PQgssEncInUse             175
+PQgetgssctx               176
diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c
index 4cbe64ceb58469200d0c2ee5a8df7eeb7c4176c8..624e02bcaae037d9e46f6ac6a7725c2c9f522b4c 100644
--- a/src/interfaces/libpq/fe-auth.c
+++ b/src/interfaces/libpq/fe-auth.c
@@ -49,52 +49,7 @@
  * GSSAPI authentication system.
  */
 
-#if defined(WIN32) && !defined(_MSC_VER)
-/*
- * 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);
-}
+#include "fe-gssapi-common.h"
 
 /*
  * Continue GSS authentication with next token as needed.
@@ -195,10 +150,7 @@ pg_GSS_continue(PGconn *conn, int payloadlen)
 static int
 pg_GSS_startup(PGconn *conn, int payloadlen)
 {
-	OM_uint32	maj_stat,
-				min_stat;
-	int			maxlen;
-	gss_buffer_desc temp_gbuf;
+	int			ret;
 	char	   *host = conn->connhost[conn->whichhost].host;
 
 	if (!(host && host[0] != '\0'))
@@ -215,33 +167,9 @@ pg_GSS_startup(PGconn *conn, int payloadlen)
 		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, host);
-	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;
-	}
+	ret = pg_GSS_load_servicename(conn);
+	if (ret != STATUS_OK)
+		return ret;
 
 	/*
 	 * Initial packet is the same as a continuation packet with no initial
@@ -977,7 +905,7 @@ pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn)
 			printfPQExpBuffer(&conn->errorMessage,
 							  libpq_gettext("SSPI authentication not supported\n"));
 			return STATUS_ERROR;
-#endif							/* !define(ENABLE_GSSAPI) */
+#endif							/* !define(ENABLE_GSS) */
 #endif							/* ENABLE_SSPI */
 
 
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index e3bf6a7449fa5dd721330ee9f8b703d567b94efd..68cf42245709f1eca8bc820a460b2ad45703ae3a 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -129,6 +129,12 @@ static int ldapServiceLookup(const char *purl, PQconninfoOption *options,
 #else
 #define DefaultSSLMode	"disable"
 #endif
+#ifdef ENABLE_GSS
+#include "fe-gssapi-common.h"
+#define DefaultGSSMode "prefer"
+#else
+#define DefaultGSSMode "disable"
+#endif
 
 /* ----------
  * Definition of the conninfo parameters and their fallback resources.
@@ -298,6 +304,14 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
 		"Require-Peer", "", 10,
 	offsetof(struct pg_conn, requirepeer)},
 
+	/*
+	 * Expose gssencmode similarly to sslmode - we can still handle "disable"
+	 * and "prefer".
+	 */
+	{"gssencmode", "PGGSSMODE", DefaultGSSMode, NULL,
+		"GSS-Mode", "", 7,		/* sizeof("disable") == 7 */
+	offsetof(struct pg_conn, gssencmode)},
+
 #if defined(ENABLE_GSS) || defined(ENABLE_SSPI)
 	/* Kerberos and GSSAPI authentication support specifying the service name */
 	{"krbsrvname", "PGKRBSRVNAME", PG_KRB_SRVNAM, NULL,
@@ -1226,6 +1240,39 @@ connectOptions2(PGconn *conn)
 			goto oom_error;
 	}
 
+	/*
+	 * validate gssencmode option
+	 */
+	if (conn->gssencmode)
+	{
+		if (strcmp(conn->gssencmode, "disable") != 0 &&
+			strcmp(conn->gssencmode, "prefer") != 0 &&
+			strcmp(conn->gssencmode, "require") != 0)
+		{
+			conn->status = CONNECTION_BAD;
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("invalid gssencmode value: \"%s\"\n"),
+							  conn->gssencmode);
+			return false;
+		}
+#ifndef ENABLE_GSS
+		if (strcmp(conn->gssencmode, "require") == 0)
+		{
+			conn->status = CONNECTION_BAD;
+			printfPQExpBuffer(
+							  &conn->errorMessage,
+							  libpq_gettext("no GSSAPI support; cannot require GSSAPI\n"));
+			return false;
+		}
+#endif
+	}
+	else
+	{
+		conn->gssencmode = strdup(DefaultGSSMode);
+		if (!conn->gssencmode)
+			goto oom_error;
+	}
+
 	/*
 	 * Resolve special "auto" client_encoding from the locale
 	 */
@@ -1827,6 +1874,11 @@ connectDBStart(PGconn *conn)
 	 */
 	resetPQExpBuffer(&conn->errorMessage);
 
+#ifdef ENABLE_GSS
+	if (conn->gssencmode[0] == 'd')	/* "disable" */
+		conn->try_gss = false;
+#endif
+
 	/*
 	 * Set up to try to connect to the first host.  (Setting whichhost = -1 is
 	 * a bit of a cheat, but PQconnectPoll will advance it to 0 before
@@ -2099,6 +2151,7 @@ PQconnectPoll(PGconn *conn)
 		case CONNECTION_NEEDED:
 		case CONNECTION_CHECK_WRITABLE:
 		case CONNECTION_CONSUME:
+		case CONNECTION_GSS_STARTUP:
 			break;
 
 		default:
@@ -2640,17 +2693,57 @@ keep_going:						/* We will come back to here until there is
 				}
 #endif							/* HAVE_UNIX_SOCKETS */
 
+				if (IS_AF_UNIX(conn->raddr.addr.ss_family))
+				{
+					/* Don't request SSL or GSSAPI over Unix sockets */
 #ifdef USE_SSL
+					conn->allow_ssl_try = false;
+#endif
+#ifdef ENABLE_GSS
+					conn->try_gss = false;
+#endif
+				}
+
+#ifdef ENABLE_GSS
 
 				/*
-				 * If SSL is enabled and we haven't already got it running,
-				 * request it instead of sending the startup message.
+				 * If GSSAPI is enabled and we have a ccache, try to set it up
+				 * before sending startup messages.  If it's already
+				 * operating, don't try SSL and instead just build the startup
+				 * packet.
 				 */
-				if (IS_AF_UNIX(conn->raddr.addr.ss_family))
+				if (conn->try_gss && !conn->gctx)
+					conn->try_gss = pg_GSS_have_ccache(&conn->gcred);
+				if (conn->try_gss && !conn->gctx)
 				{
-					/* Don't bother requesting SSL over a Unix socket */
-					conn->allow_ssl_try = false;
+					ProtocolVersion pv = pg_hton32(NEGOTIATE_GSS_CODE);
+
+					if (pqPacketSend(conn, 0, &pv, sizeof(pv)) != STATUS_OK)
+					{
+						appendPQExpBuffer(&conn->errorMessage,
+										  libpq_gettext("could not send GSSAPI negotiation packet: %s\n"),
+										  SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+						goto error_return;
+					}
+
+					/* Ok, wait for response */
+					conn->status = CONNECTION_GSS_STARTUP;
+					return PGRES_POLLING_READING;
 				}
+				else if (!conn->gctx && conn->gssencmode[0] == 'r')
+				{
+					appendPQExpBuffer(&conn->errorMessage,
+									  libpq_gettext("GSSAPI encryption required, but was impossible (possibly no ccache, no server support, or using a local socket)\n"));
+					goto error_return;
+				}
+#endif
+
+#ifdef USE_SSL
+
+				/*
+				 * If SSL is enabled and we haven't already got it running,
+				 * request it instead of sending the startup message.
+				 */
 				if (conn->allow_ssl_try && !conn->wait_ssl_try &&
 					!conn->ssl_in_use)
 				{
@@ -2844,6 +2937,98 @@ keep_going:						/* We will come back to here until there is
 #endif							/* USE_SSL */
 			}
 
+		case CONNECTION_GSS_STARTUP:
+			{
+#ifdef ENABLE_GSS
+				PostgresPollingStatusType pollres;
+
+				/*
+				 * If we haven't yet, get the postmaster's response to our
+				 * negotiation packet
+				 */
+				if (conn->try_gss && !conn->gctx)
+				{
+					char		gss_ok;
+					int			rdresult = pqReadData(conn);
+
+					if (rdresult < 0)
+						/* pqReadData fills in error message */
+						goto error_return;
+					else if (rdresult == 0)
+						/* caller failed to wait for data */
+						return PGRES_POLLING_READING;
+					if (pqGetc(&gss_ok, conn) < 0)
+						/* shouldn't happen... */
+						return PGRES_POLLING_READING;
+
+					if (gss_ok == 'E')
+					{
+						/*
+						 * Server failure of some sort.  Assume it's a
+						 * protocol version support failure, and let's see if
+						 * we can't recover (if it's not, we'll get a better
+						 * error message on retry).  Server gets fussy if we
+						 * don't hang up the socket, though.
+						 */
+						conn->try_gss = false;
+						pqDropConnection(conn, true);
+						conn->status = CONNECTION_NEEDED;
+						goto keep_going;
+					}
+
+					/* mark byte consumed */
+					conn->inStart = conn->inCursor;
+
+					if (gss_ok == 'N')
+					{
+						/* Server doesn't want GSSAPI; fall back if we can */
+						if (conn->gssencmode[0] == 'r')
+						{
+							appendPQExpBufferStr(&conn->errorMessage,
+												 libpq_gettext("server doesn't support GSSAPI encryption, but it was required\n"));
+							goto error_return;
+						}
+
+						conn->try_gss = false;
+						conn->status = CONNECTION_MADE;
+						return PGRES_POLLING_WRITING;
+					}
+					else if (gss_ok != 'G')
+					{
+						appendPQExpBuffer(&conn->errorMessage,
+										  libpq_gettext("received invalid response to GSSAPI negotiation: %c\n"),
+										  gss_ok);
+						goto error_return;
+					}
+				}
+
+				/* Begin or continue GSSAPI negotiation */
+				pollres = pqsecure_open_gss(conn);
+				if (pollres == PGRES_POLLING_OK)
+				{
+					/* All set for startup packet */
+					conn->status = CONNECTION_MADE;
+					return PGRES_POLLING_WRITING;
+				}
+				else if (pollres == PGRES_POLLING_FAILED &&
+						 conn->gssencmode[0] == 'p')
+				{
+					/*
+					 * We failed, but we can retry on "prefer".  Have to drop
+					 * the current connection to do so, though.
+					 */
+					conn->try_gss = false;
+					pqDropConnection(conn, true);
+					conn->status = CONNECTION_NEEDED;
+					goto keep_going;
+				}
+				return pollres;
+#else							/* !ENABLE_GSS */
+				/* unreachable */
+				goto error_return;
+#endif							/* ENABLE_GSS */
+			}
+
 			/*
 			 * Handle authentication exchange: wait for postmaster messages
 			 * and respond as necessary.
@@ -2997,6 +3182,26 @@ keep_going:						/* We will come back to here until there is
 					/* Check to see if we should mention pgpassfile */
 					pgpassfileWarning(conn);
 
+#ifdef ENABLE_GSS
+
+					/*
+					 * If gssencmode is "prefer" and we're using GSSAPI, retry
+					 * without it.
+					 */
+					if (conn->gssenc && conn->gssencmode[0] == 'p')
+					{
+						OM_uint32	minor;
+
+						/* postmaster expects us to drop the connection */
+						conn->try_gss = false;
+						conn->gssenc = false;
+						gss_delete_sec_context(&minor, &conn->gctx, NULL);
+						pqDropConnection(conn, true);
+						conn->status = CONNECTION_NEEDED;
+						goto keep_going;
+					}
+#endif
+
 #ifdef USE_SSL
 
 					/*
@@ -3564,6 +3769,9 @@ makeEmptyPGconn(void)
 	conn->verbosity = PQERRORS_DEFAULT;
 	conn->show_context = PQSHOW_CONTEXT_ERRORS;
 	conn->sock = PGINVALID_SOCKET;
+#ifdef ENABLE_GSS
+	conn->try_gss = true;
+#endif
 
 	/*
 	 * We try to send at least 8K at a time, which is the usual size of pipe
@@ -3695,10 +3903,28 @@ freePGconn(PGconn *conn)
 		free(conn->requirepeer);
 	if (conn->connip)
 		free(conn->connip);
+	if (conn->gssencmode)
+		free(conn->gssencmode);
 #if defined(ENABLE_GSS) || defined(ENABLE_SSPI)
 	if (conn->krbsrvname)
 		free(conn->krbsrvname);
 #endif
+#ifdef ENABLE_GSS
+	if (conn->gcred != GSS_C_NO_CREDENTIAL)
+	{
+		OM_uint32	minor;
+
+		gss_release_cred(&minor, &conn->gcred);
+		conn->gcred = GSS_C_NO_CREDENTIAL;
+	}
+	if (conn->gctx)
+	{
+		OM_uint32	minor;
+
+		gss_delete_sec_context(&minor, &conn->gctx, GSS_C_NO_BUFFER);
+		conn->gctx = NULL;
+	}
+#endif
 #if defined(ENABLE_GSS) && defined(ENABLE_SSPI)
 	if (conn->gsslib)
 		free(conn->gsslib);
diff --git a/src/interfaces/libpq/fe-gssapi-common.c b/src/interfaces/libpq/fe-gssapi-common.c
new file mode 100644
index 0000000000000000000000000000000000000000..3192f9190b5ad49efe2992a4a9ca21a729b0ae3f
--- /dev/null
+++ b/src/interfaces/libpq/fe-gssapi-common.c
@@ -0,0 +1,130 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-gssapi-common.c
+ *     The front-end (client) GSSAPI common code
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *      src/interfaces/libpq/fe-gssapi-common.c
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres_fe.h"
+
+#include "fe-gssapi-common.h"
+
+#include "libpq-int.h"
+#include "pqexpbuffer.h"
+
+/*
+ * 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.
+ */
+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);
+}
+
+/*
+ * Check if we can acquire credentials at all (and yield them if so).
+ */
+bool
+pg_GSS_have_ccache(gss_cred_id_t *cred_out)
+{
+	OM_uint32	major,
+				minor;
+	gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
+
+	major = gss_acquire_cred(&minor, GSS_C_NO_NAME, 0, GSS_C_NO_OID_SET,
+							 GSS_C_INITIATE, &cred, NULL, NULL);
+	if (major != GSS_S_COMPLETE)
+	{
+		*cred_out = NULL;
+		return false;
+	}
+	*cred_out = cred;
+	return true;
+}
+
+/*
+ * Try to load service name for a connection
+ */
+int
+pg_GSS_load_servicename(PGconn *conn)
+{
+	OM_uint32	maj_stat,
+				min_stat;
+	int			maxlen;
+	gss_buffer_desc temp_gbuf;
+	char	   *host;
+
+	if (conn->gtarg_nam != NULL)
+		/* Already taken care of - move along */
+		return STATUS_OK;
+
+	host = PQhost(conn);
+	if (!(host && host[0] != '\0'))
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("host name must be specified\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, host);
+	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;
+	}
+	return STATUS_OK;
+}
diff --git a/src/interfaces/libpq/fe-gssapi-common.h b/src/interfaces/libpq/fe-gssapi-common.h
new file mode 100644
index 0000000000000000000000000000000000000000..b429e79848bf16efabe0360aa37bb2c1b2a65c43
--- /dev/null
+++ b/src/interfaces/libpq/fe-gssapi-common.h
@@ -0,0 +1,23 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-gssapi-common.h
+ *
+ *      Definitions for GSSAPI common routines
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/interfaces/libpq/fe-gssapi-common.h
+ */
+
+#ifndef FE_GSSAPI_COMMON_H
+#define FE_GSSAPI_COMMON_H
+
+#include "libpq-fe.h"
+#include "libpq-int.h"
+
+void pg_GSS_error(const char *mprefix, PGconn *conn,
+			 OM_uint32 maj_stat, OM_uint32 min_stat);
+bool		pg_GSS_have_ccache(gss_cred_id_t *cred_out);
+int			pg_GSS_load_servicename(PGconn *conn);
+#endif							/* FE_GSSAPI_COMMON_H */
diff --git a/src/interfaces/libpq/fe-secure-gssapi.c b/src/interfaces/libpq/fe-secure-gssapi.c
new file mode 100644
index 0000000000000000000000000000000000000000..ea1c1cd7b71f587928d450b5e7da6deb5cb945eb
--- /dev/null
+++ b/src/interfaces/libpq/fe-secure-gssapi.c
@@ -0,0 +1,635 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-secure-gssapi.c
+ *   The front-end (client) encryption support for GSSAPI
+ *
+ * Portions Copyright (c) 2016-2018, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *  src/interfaces/libpq/fe-secure-gssapi.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres_fe.h"
+
+#include "libpq-fe.h"
+#include "libpq-int.h"
+#include "fe-gssapi-common.h"
+
+#include "port/pg_bswap.h"
+
+/*
+ * Require encryption support, as well as mutual authentication and
+ * tamperproofing measures.
+ */
+#define GSS_REQUIRED_FLAGS GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | \
+	GSS_C_SEQUENCE_FLAG | GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG
+
+/*
+ * We use fixed-size buffers for handling the encryption/decryption
+ * which are larger than PQComm's buffer will typically be to minimize
+ * the times where we have to make multiple packets and therefore sets
+ * of recv/send calls for a single read/write call to us.
+ *
+ * NOTE: The client and server have to agree on the max packet size,
+ * because we have to pass an entire packet to GSSAPI at a time and we
+ * don't want the other side to send arbitrairly huge packets as we
+ * would have to allocate memory for them to then pass them to GSSAPI.
+ */
+#define PQ_GSS_SEND_BUFFER_SIZE 16384
+#define PQ_GSS_RECV_BUFFER_SIZE 16384
+
+/* PqGSSSendBuffer is for *encrypted* data */
+static char PqGSSSendBuffer[PQ_GSS_SEND_BUFFER_SIZE];
+static int	PqGSSSendPointer;	/* Next index to store a byte in
+								 * PqGSSSendBuffer */
+static int	PqGSSSendStart;		/* Next index to send a byte in
+								 * PqGSSSendBuffer */
+
+/* PqGSSRecvBuffer is for *encrypted* data */
+static char PqGSSRecvBuffer[PQ_GSS_RECV_BUFFER_SIZE];
+static int	PqGSSRecvPointer;	/* Next index to read a byte from
+								 * PqGSSRecvBuffer */
+static int	PqGSSRecvLength;	/* End of data available in PqGSSRecvBuffer */
+
+/* PqGSSResultBuffer is for *unencrypted* data */
+static char PqGSSResultBuffer[PQ_GSS_RECV_BUFFER_SIZE];
+static int	PqGSSResultPointer; /* Next index to read a byte from
+								 * PqGSSResultBuffer */
+static int	PqGSSResultLength;	/* End of data available in PqGSSResultBuffer */
+
+uint32		max_packet_size;	/* Maximum size we can encrypt and fit the
+								 * results into our output buffer */
+
+/*
+ * Write len bytes of data from ptr along a GSSAPI-encrypted connection.  Note
+ * that the connection must be already set up for GSSAPI encryption (i.e.,
+ * GSSAPI transport negotiation is complete).  Returns len when all data has
+ * been written; retry when errno is EWOULDBLOCK or similar with the same
+ * values of ptr and len.  On non-socket failures, will log an error message.
+ */
+ssize_t
+pg_GSS_write(PGconn *conn, const void *ptr, size_t len)
+{
+	gss_buffer_desc input,
+				output = GSS_C_EMPTY_BUFFER;
+	OM_uint32	major,
+				minor;
+	ssize_t		ret = -1;
+	size_t		bytes_to_encrypt = len;
+	size_t		bytes_encrypted = 0;
+
+	/*
+	 * Loop through encrypting data and sending it out until
+	 * pqsecure_raw_write() complains (which would likely mean that the socket
+	 * is non-blocking and the requested send() would block, or there was some
+	 * kind of actual error) and then return.
+	 */
+	while (bytes_to_encrypt || PqGSSSendPointer)
+	{
+		int			conf = 0;
+		uint32		netlen;
+
+		/*
+		 * Check if we have data in the encrypted output buffer that needs to
+		 * be sent, and if so, try to send it.  If we aren't able to, return
+		 * that back up to the caller.
+		 */
+		if (PqGSSSendPointer)
+		{
+			ssize_t		ret;
+			ssize_t		amount = PqGSSSendPointer - PqGSSSendStart;
+
+			ret = pqsecure_raw_write(conn, PqGSSSendBuffer + PqGSSSendStart, amount);
+			if (ret < 0)
+			{
+				/*
+				 * If we encrypted some data and it's in our output buffer,
+				 * but send() is saying that we would block, then tell the
+				 * client how far we got with encrypting the data so that they
+				 * can call us again with whatever is left, at which point we
+				 * will try to send the remaining encrypted data first and
+				 * then move on to encrypting the rest of the data.
+				 */
+				if (bytes_encrypted != 0 && (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR))
+					return bytes_encrypted;
+				else
+					return ret;
+			}
+
+			/*
+			 * Partial write, move forward that far in our buffer and try
+			 * again
+			 */
+			if (ret != amount)
+			{
+				PqGSSSendStart += ret;
+				continue;
+			}
+
+			/* All encrypted data was sent, our buffer is empty now. */
+			PqGSSSendPointer = PqGSSSendStart = 0;
+		}
+
+		/*
+		 * Check if there are any bytes left to encrypt.  If not, we're done.
+		 */
+		if (!bytes_to_encrypt)
+			return bytes_encrypted;
+
+		/*
+		 * Check how much we are being asked to send, if it's too much, then
+		 * we will have to loop and possibly be called multiple times to get
+		 * through all the data.
+		 */
+		if (bytes_to_encrypt > max_packet_size)
+			input.length = max_packet_size;
+		else
+			input.length = bytes_to_encrypt;
+
+		input.value = (char *) ptr + bytes_encrypted;
+
+		output.value = NULL;
+		output.length = 0;
+
+		/* Create the next encrypted packet */
+		major = gss_wrap(&minor, conn->gctx, 1, GSS_C_QOP_DEFAULT,
+						 &input, &conf, &output);
+		if (major != GSS_S_COMPLETE)
+		{
+			pg_GSS_error(libpq_gettext("GSSAPI wrap error"), conn, major, minor);
+			goto cleanup;
+		}
+		else if (conf == 0)
+		{
+			printfPQExpBuffer(&conn->errorMessage, libpq_gettext(
+																 "GSSAPI did not provide confidentiality\n"));
+			goto cleanup;
+		}
+
+		if (output.length > PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))
+		{
+			printfPQExpBuffer(&conn->errorMessage, libpq_gettext(
+																 "GSSAPI attempt to send oversize packet\n"));
+			goto cleanup;
+		}
+
+		bytes_encrypted += input.length;
+		bytes_to_encrypt -= input.length;
+
+		/* 4 network-order bytes of length, then payload */
+		netlen = htonl(output.length);
+		memcpy(PqGSSSendBuffer + PqGSSSendPointer, &netlen, sizeof(uint32));
+		PqGSSSendPointer += sizeof(uint32);
+
+		memcpy(PqGSSSendBuffer + PqGSSSendPointer, output.value, output.length);
+		PqGSSSendPointer += output.length;
+	}
+
+	ret = bytes_encrypted;
+
+cleanup:
+	if (output.value != NULL)
+		gss_release_buffer(&minor, &output);
+	return ret;
+}
+
+/*
+ * Read up to len bytes of data into ptr from a GSSAPI-encrypted connection.
+ * Note that GSSAPI transport must already have been negotiated.  Returns the
+ * number of bytes read into ptr; otherwise, returns -1.  Retry with the same
+ * ptr and len when errno is EWOULDBLOCK or similar.
+ */
+ssize_t
+pg_GSS_read(PGconn *conn, void *ptr, size_t len)
+{
+	OM_uint32	major,
+				minor;
+	gss_buffer_desc input = GSS_C_EMPTY_BUFFER,
+				output = GSS_C_EMPTY_BUFFER;
+	ssize_t		ret = 0;
+	size_t		bytes_to_return = len;
+	size_t		bytes_returned = 0;
+
+	/*
+	 * The goal here is to read an incoming encrypted packet, one at a time,
+	 * decrypt it into our out buffer, returning to the caller what they asked
+	 * for, and then saving anything else for the next call.
+	 *
+	 * We get a read request, we look if we have cleartext bytes available
+	 * and, if so, copy those to the result, and then we try to decrypt the
+	 * next packet.
+	 *
+	 * We should not try to decrypt the next packet until the read buffer is
+	 * completely empty.
+	 *
+	 * If the caller asks for more bytes than one decrypted packet, then we
+	 * should try to return all bytes asked for.
+	 */
+	while (bytes_to_return)
+	{
+		int			conf = 0;
+
+		/* Check if we have data in our buffer that we can return immediately */
+		if (PqGSSResultPointer < PqGSSResultLength)
+		{
+			int			bytes_in_buffer = PqGSSResultLength - PqGSSResultPointer;
+			int			bytes_to_copy = bytes_in_buffer < len - bytes_returned ? bytes_in_buffer : len - bytes_returned;
+
+			/*
+			 * Copy the data from our output buffer into the caller's buffer,
+			 * at the point where we last left off filling their buffer
+			 */
+			memcpy((char *) ptr + bytes_returned, PqGSSResultBuffer + PqGSSResultPointer, bytes_to_copy);
+			PqGSSResultPointer += bytes_to_copy;
+			bytes_to_return -= bytes_to_copy;
+			bytes_returned += bytes_to_copy;
+
+			/* Check if our result buffer is now empty and, if so, reset */
+			if (PqGSSResultPointer == PqGSSResultLength)
+				PqGSSResultPointer = PqGSSResultLength = 0;
+
+			continue;
+		}
+
+		/*
+		 * At this point, our output buffer should be empty with more bytes
+		 * being requested to be read.  We are now ready to load the next
+		 * packet and decrypt it (entirely) into our buffer.
+		 *
+		 * If we get a partial read back while trying to read a packet off the
+		 * wire then we return back what bytes we were able to return and wait
+		 * to be called again, until we get a full packet to decrypt.
+		 */
+
+		/* Check if we got a partial read just trying to get the length */
+		if (PqGSSRecvLength < sizeof(uint32))
+		{
+			/* Try to get whatever of the length we still need */
+			ret = pqsecure_raw_read(conn, PqGSSRecvBuffer + PqGSSRecvLength,
+									sizeof(uint32) - PqGSSRecvLength);
+			if (ret < 0)
+				return bytes_returned ? bytes_returned : ret;
+
+			PqGSSRecvLength += ret;
+			if (PqGSSRecvLength < sizeof(uint32))
+				return bytes_returned;
+		}
+
+		/*
+		 * We should have the whole length at this point, so pull it out and
+		 * then read whatever we have left of the packet
+		 */
+		input.length = ntohl(*(uint32 *) PqGSSRecvBuffer);
+
+		/* Check for over-length packet */
+		if (input.length > PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32))
+		{
+			printfPQExpBuffer(&conn->errorMessage, libpq_gettext(
+																 "GSSAPI did not provide confidentiality\n"));
+			ret = -1;
+			goto cleanup;
+		}
+
+		/*
+		 * Read as much of the packet as we are able to on this call into
+		 * wherever we left off from the last time we were called.
+		 */
+		ret = pqsecure_raw_read(conn, PqGSSRecvBuffer + PqGSSRecvLength,
+								input.length - (PqGSSRecvLength - sizeof(uint32)));
+		if (ret < 0)
+			return bytes_returned ? bytes_returned : ret;
+
+		/*
+		 * If we got less than the rest of the packet then we need to return
+		 * and be called again.
+		 */
+		PqGSSRecvLength += ret;
+		if (PqGSSRecvLength - sizeof(uint32) < input.length)
+			return bytes_returned ? bytes_returned : -1;
+
+		/*
+		 * We now have the full packet and we can perform the decryption and
+		 * refill our output buffer, then loop back up to pass that back to
+		 * the user.
+		 */
+		output.value = NULL;
+		output.length = 0;
+		input.value = PqGSSRecvBuffer + sizeof(uint32);
+
+		major = gss_unwrap(&minor, conn->gctx, &input, &output, &conf, NULL);
+		if (major != GSS_S_COMPLETE)
+		{
+			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;
+		}
+
+		memcpy(PqGSSResultBuffer, output.value, output.length);
+		PqGSSResultLength = output.length;
+
+		/* Our buffer is now empty, reset it */
+		PqGSSRecvPointer = PqGSSRecvLength = 0;
+
+		gss_release_buffer(&minor, &output);
+	}
+
+	ret = bytes_returned;
+
+cleanup:
+	if (output.value != NULL)
+		gss_release_buffer(&minor, &output);
+	return ret;
+}
+
+/*
+ * Simple wrapper for reading from pqsecure_raw_read.
+ *
+ * This takes the same arguments as pqsecure_raw_read, plus an output parameter
+ * to return the number of bytes read.  This handles if blocking would occur and
+ * if we detect EOF on the connection.
+ */
+static PostgresPollingStatusType
+gss_read(PGconn *conn, void *recv_buffer, size_t length, ssize_t *ret)
+{
+	*ret = pqsecure_raw_read(conn, recv_buffer, length);
+	if (*ret < 0 && errno == EWOULDBLOCK)
+		return PGRES_POLLING_READING;
+	else if (*ret < 0)
+		return PGRES_POLLING_FAILED;
+
+	/* Check for EOF */
+	if (*ret == 0)
+	{
+		int			result = pqReadReady(conn);
+
+		if (result < 0)
+			return PGRES_POLLING_FAILED;
+
+		if (!result)
+			return PGRES_POLLING_READING;
+
+		*ret = pqsecure_raw_read(conn, recv_buffer, length);
+		if (*ret == 0)
+			return PGRES_POLLING_FAILED;
+	}
+
+	return PGRES_POLLING_OK;
+}
+
+/*
+ * Negotiate GSSAPI transport for a connection.  When complete, returns
+ * PGRES_POLLING_OK.  Will return PGRES_POLLING_READING or
+ * PGRES_POLLING_WRITING as appropriate whenever it would block, and
+ * PGRES_POLLING_FAILED if transport could not be negotiated.
+ */
+PostgresPollingStatusType
+pqsecure_open_gss(PGconn *conn)
+{
+	static int	first = 1;
+	ssize_t		ret;
+	OM_uint32	major,
+				minor;
+	uint32		netlen;
+	PostgresPollingStatusType result;
+	gss_buffer_desc input = GSS_C_EMPTY_BUFFER,
+				output = GSS_C_EMPTY_BUFFER;
+
+	/* Check for data that needs to be written */
+	if (first)
+	{
+		PqGSSSendPointer = PqGSSSendStart = PqGSSRecvPointer = PqGSSRecvLength = PqGSSResultPointer = PqGSSResultLength = 0;
+		first = 0;
+	}
+
+	/*
+	 * Check if we have anything to send from a prior call and if so, send it.
+	 */
+	if (PqGSSSendPointer)
+	{
+		ssize_t		amount = PqGSSSendPointer - PqGSSSendStart;
+
+		ret = pqsecure_raw_write(conn, PqGSSSendBuffer + PqGSSSendStart, amount);
+		if (ret < 0 && errno == EWOULDBLOCK)
+			return PGRES_POLLING_WRITING;
+
+		if (ret != amount)
+		{
+			PqGSSSendStart += amount;
+			return PGRES_POLLING_WRITING;
+		}
+
+		PqGSSSendPointer = PqGSSSendStart = 0;
+	}
+
+	/*
+	 * Client sends first, and sending creates a context, therefore this will
+	 * be false the first time through, and then when we get called again we
+	 * will check for incoming data.
+	 */
+	if (conn->gctx)
+	{
+		/* Process any incoming data we might have */
+
+		/* See if we are still trying to get the length */
+		if (PqGSSRecvLength < sizeof(uint32))
+		{
+			/* Attempt to get the length first */
+			result = gss_read(conn, PqGSSRecvBuffer + PqGSSRecvLength, sizeof(uint32) - PqGSSRecvLength, &ret);
+			if (result != PGRES_POLLING_OK)
+				return result;
+
+			PqGSSRecvLength += ret;
+
+			if (PqGSSRecvLength < sizeof(uint32))
+				return PGRES_POLLING_READING;
+		}
+
+		/*
+		 * Check if we got an error packet
+		 *
+		 * This is safe to do because we shouldn't ever get a packet over 8192
+		 * and therefore the actual length bytes, being that they are in
+		 * network byte order, for any real packet will be two zero bytes.
+		 */
+		if (PqGSSRecvBuffer[0] == 'E')
+		{
+			/*
+			 * For an error message, the length is after the E, so read one
+			 * more byte to get the full length
+			 */
+			result = gss_read(conn, PqGSSRecvBuffer + PqGSSRecvLength, 1, &ret);
+			if (result != PGRES_POLLING_OK)
+				return result;
+
+			PqGSSRecvLength += ret;
+
+			if (PqGSSRecvLength < 1 + sizeof(uint32))
+				return PGRES_POLLING_READING;
+
+			input.length = ntohl(*(uint32 *) PqGSSRecvBuffer + 1);
+			if (input.length > PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32) - 1)
+			{
+				printfPQExpBuffer(&conn->errorMessage, libpq_gettext("Over-size error packet sent by the server."));
+				return PGRES_POLLING_FAILED;
+			}
+
+			result = gss_read(conn, PqGSSRecvBuffer + PqGSSRecvLength, input.length - PqGSSRecvLength - 1 - sizeof(uint32), &ret);
+			if (result != PGRES_POLLING_OK)
+				return result;
+
+			PqGSSRecvLength += ret;
+
+			if (PqGSSRecvLength < 1 + sizeof(uint32) + input.length)
+				return PGRES_POLLING_READING;
+
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("Server error: %s"),
+							  PqGSSRecvBuffer + 1 + sizeof(int32));
+
+			return PGRES_POLLING_FAILED;
+		}
+
+		/*
+		 * We should have the whole length at this point, so pull it out and
+		 * then read whatever we have left of the packet
+		 */
+
+		/* Get the length and check for over-length packet */
+		input.length = ntohl(*(uint32 *) PqGSSRecvBuffer);
+		if (input.length > PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32))
+		{
+			printfPQExpBuffer(&conn->errorMessage, libpq_gettext("Over-size GSSAPI packet sent by the server: %ld"), input.length);
+			return PGRES_POLLING_FAILED;
+		}
+
+		/*
+		 * Read as much of the packet as we are able to on this call into
+		 * wherever we left off from the last time we were called.
+		 */
+		result = gss_read(conn, PqGSSRecvBuffer + PqGSSRecvLength,
+						  input.length - (PqGSSRecvLength - sizeof(uint32)), &ret);
+		if (result != PGRES_POLLING_OK)
+			return result;
+
+		PqGSSRecvLength += ret;
+
+		/*
+		 * If we got less than the rest of the packet then we need to return
+		 * and be called again.
+		 */
+		if (PqGSSRecvLength - sizeof(uint32) < input.length)
+			return PGRES_POLLING_READING;
+
+		input.value = PqGSSRecvBuffer + sizeof(uint32);
+	}
+
+	/* Load the service name (no-op if already done */
+	ret = pg_GSS_load_servicename(conn);
+	if (ret != STATUS_OK)
+		return PGRES_POLLING_FAILED;
+
+	/*
+	 * Call GSS init context, either with an empty input, or with a complete
+	 * packet from the server.
+	 */
+	major = gss_init_sec_context(&minor, conn->gcred, &conn->gctx,
+								 conn->gtarg_nam, GSS_C_NO_OID,
+								 GSS_REQUIRED_FLAGS, 0, 0, &input, NULL,
+								 &output, NULL, NULL);
+
+	/* GSS Init Sec Context uses the whole packet, so clear it */
+	PqGSSRecvPointer = PqGSSRecvLength = 0;
+
+	if (GSS_ERROR(major))
+	{
+		pg_GSS_error(libpq_gettext("GSSAPI context establishment error"),
+					 conn, major, minor);
+		return PGRES_POLLING_FAILED;
+	}
+	else if (output.length == 0)
+	{
+		/*
+		 * We're done - hooray!  Kind of gross, but we need to disable SSL
+		 * here so that we don't accidentally tunnel one over the other.
+		 */
+#ifdef USE_SSL
+		conn->allow_ssl_try = false;
+#endif
+		gss_release_cred(&minor, &conn->gcred);
+		conn->gcred = GSS_C_NO_CREDENTIAL;
+		conn->gssenc = true;
+
+		/*
+		 * Determine the max packet size which will fit in our buffer, after
+		 * accounting for the length
+		 */
+		major = gss_wrap_size_limit(&minor, conn->gctx, 1, GSS_C_QOP_DEFAULT,
+									PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32), &max_packet_size);
+
+		if (GSS_ERROR(major))
+			pg_GSS_error(libpq_gettext("GSSAPI size check error"), conn,
+						 major, minor);
+
+		return PGRES_POLLING_OK;
+	}
+
+	/* Must have output.length > 0 */
+	if (output.length > PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))
+	{
+		pg_GSS_error(libpq_gettext("GSSAPI context establishment error"),
+					 conn, major, minor);
+		return PGRES_POLLING_FAILED;
+	}
+
+	/* Queue the token for writing */
+	netlen = htonl(output.length);
+
+	memcpy(PqGSSSendBuffer, (char *) &netlen, sizeof(uint32));
+	PqGSSSendPointer += sizeof(uint32);
+
+	memcpy(PqGSSSendBuffer + PqGSSSendPointer, output.value, output.length);
+	PqGSSSendPointer += output.length;
+
+	gss_release_buffer(&minor, &output);
+
+	/* Asked to be called again to write data */
+	return PGRES_POLLING_WRITING;
+}
+
+/*
+ * GSSAPI Information functions.
+ */
+
+/*
+ * Return the GSSAPI Context itself.
+ */
+void *
+PQgetgssctx(PGconn *conn)
+{
+	if (!conn)
+		return NULL;
+
+	return conn->gctx;
+}
+
+/*
+ * Return true if GSSAPI encryption is in use.
+ */
+int
+PQgssEncInUse(PGconn *conn)
+{
+	if (!conn || !conn->gctx)
+		return 0;
+
+	return conn->gssenc;
+}
diff --git a/src/interfaces/libpq/fe-secure.c b/src/interfaces/libpq/fe-secure.c
index 4658e27caa68f4de6133fefcdd95d37772f3b8e4..b8191b4c8f8a8f5314745b0fd97de8150a300d06 100644
--- a/src/interfaces/libpq/fe-secure.c
+++ b/src/interfaces/libpq/fe-secure.c
@@ -220,6 +220,13 @@ pqsecure_read(PGconn *conn, void *ptr, size_t len)
 		n = pgtls_read(conn, ptr, len);
 	}
 	else
+#endif
+#ifdef ENABLE_GSS
+	if (conn->gssenc)
+	{
+		n = pg_GSS_read(conn, ptr, len);
+	}
+	else
 #endif
 	{
 		n = pqsecure_raw_read(conn, ptr, len);
@@ -297,6 +304,13 @@ pqsecure_write(PGconn *conn, const void *ptr, size_t len)
 		n = pgtls_write(conn, ptr, len);
 	}
 	else
+#endif
+#ifdef ENABLE_GSS
+	if (conn->gssenc)
+	{
+		n = pg_GSS_write(conn, ptr, len);
+	}
+	else
 #endif
 	{
 		n = pqsecure_raw_write(conn, ptr, len);
@@ -420,6 +434,23 @@ PQsslAttributeNames(PGconn *conn)
 }
 #endif							/* USE_SSL */
 
+/* Dummy version of GSSAPI information functions, when built without GSS support */
+#ifndef ENABLE_GSS
+
+void *
+PQgetgssctx(PGconn *conn)
+{
+	return NULL;
+}
+
+int
+PQgssEncInUse(PGconn *conn)
+{
+	return 0;
+}
+
+#endif							/* ENABLE_GSS */
+
 
 #if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
 
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index 97bc98b1f3cb05d67e9b2192c773f8abc7ca2be3..27047ddd1f3b5089bd6259d5461b6143c7cadc06 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -65,8 +65,9 @@ typedef enum
 	CONNECTION_NEEDED,			/* Internal state: connect() needed */
 	CONNECTION_CHECK_WRITABLE,	/* Check if we could make a writable
 								 * connection. */
-	CONNECTION_CONSUME			/* Wait for any pending message and consume
+	CONNECTION_CONSUME,			/* Wait for any pending message and consume
 								 * them. */
+	CONNECTION_GSS_STARTUP		/* Negotiating GSSAPI. */
 } ConnStatusType;
 
 typedef enum
@@ -346,6 +347,12 @@ extern void PQinitSSL(int do_init);
 /* More detailed way to tell libpq whether it needs to initialize OpenSSL */
 extern void PQinitOpenSSL(int do_ssl, int do_crypto);
 
+/* Return true if GSSAPI encryption is in use */
+extern int PQgssEncInUse(PGconn *conn);
+
+/* Returns GSSAPI context if GSSAPI is in use */
+extern void *PQgetgssctx(PGconn *conn);
+
 /* Set verbosity for PQerrorMessage and PQresultErrorMessage */
 extern PGVerbosity PQsetErrorVerbosity(PGconn *conn, PGVerbosity verbosity);
 
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index dbe0f7e5c0bb73415061d130eacc9d0eacd504f2..84222f2c7ca0082818bcd30ac2c8d2f1a8c1371e 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -480,9 +480,15 @@ struct pg_conn
 #endif							/* USE_OPENSSL */
 #endif							/* USE_SSL */
 
+	char	   *gssencmode;		/* GSS mode (require,prefer,disable) */
 #ifdef ENABLE_GSS
 	gss_ctx_id_t gctx;			/* GSS context */
 	gss_name_t	gtarg_nam;		/* GSS target name */
+
+	/* The following are encryption-only */
+	bool		try_gss;		/* GSS attempting permitted */
+	bool		gssenc;			/* GSS encryption is usable */
+	gss_cred_id_t gcred;		/* GSS credential temp storage. */
 #endif
 
 #ifdef ENABLE_SSPI
@@ -749,6 +755,23 @@ extern int pgtls_verify_peer_name_matches_certificate_guts(PGconn *conn,
 												int *names_examined,
 												char **first_name);
 
+/* === GSSAPI === */
+
+#ifdef ENABLE_GSS
+
+/*
+ * Establish a GSSAPI-encrypted connection.
+ */
+extern PostgresPollingStatusType pqsecure_open_gss(PGconn *conn);
+
+/*
+ * Read and write functions for GSSAPI-encrypted connections, with internal
+ * buffering to handle nonblocking sockets.
+ */
+extern ssize_t pg_GSS_write(PGconn *conn, const void *ptr, size_t len);
+extern ssize_t pg_GSS_read(PGconn *conn, void *ptr, size_t len);
+#endif
+
 /* === miscellaneous macros === */
 
 /*
diff --git a/src/test/kerberos/t/002_enc.pl b/src/test/kerberos/t/002_enc.pl
new file mode 100644
index 0000000000000000000000000000000000000000..1a7dc30a810e010db087702b20d49b49cff6b978
--- /dev/null
+++ b/src/test/kerberos/t/002_enc.pl
@@ -0,0 +1,197 @@
+use strict;
+use warnings;
+use TestLib;
+use PostgresNode;
+use Test::More;
+use File::Path 'remove_tree';
+
+if ($ENV{with_gssapi} eq 'yes')
+{
+	plan tests => 5;
+}
+else
+{
+	plan skip_all => 'GSSAPI/Kerberos not supported by this build';
+}
+
+my ($krb5_bin_dir, $krb5_sbin_dir);
+
+if ($^O eq 'darwin')
+{
+	$krb5_bin_dir  = '/usr/local/opt/krb5/bin';
+	$krb5_sbin_dir = '/usr/local/opt/krb5/sbin';
+}
+elsif ($^O eq 'freebsd')
+{
+	$krb5_bin_dir  = '/usr/local/bin';
+	$krb5_sbin_dir = '/usr/local/sbin';
+}
+elsif ($^O eq 'linux')
+{
+	$krb5_sbin_dir = '/usr/sbin';
+}
+
+my $krb5_config  = 'krb5-config';
+my $kinit        = 'kinit';
+my $kdb5_util    = 'kdb5_util';
+my $kadmin_local = 'kadmin.local';
+my $krb5kdc      = 'krb5kdc';
+
+if ($krb5_bin_dir && -d $krb5_bin_dir)
+{
+	$krb5_config = $krb5_bin_dir . '/' . $krb5_config;
+	$kinit       = $krb5_bin_dir . '/' . $kinit;
+}
+if ($krb5_sbin_dir && -d $krb5_sbin_dir)
+{
+	$kdb5_util    = $krb5_sbin_dir . '/' . $kdb5_util;
+	$kadmin_local = $krb5_sbin_dir . '/' . $kadmin_local;
+	$krb5kdc      = $krb5_sbin_dir . '/' . $krb5kdc;
+}
+
+my $host     = 'auth-test-localhost.postgresql.example.com';
+my $hostaddr = '127.0.0.1';
+my $realm = 'EXAMPLE.COM';
+
+my $krb5_conf   = "${TestLib::tmp_check}/krb5.conf";
+my $kdc_conf    = "${TestLib::tmp_check}/kdc.conf";
+my $krb5_log    = "${TestLib::tmp_check}/krb5libs.log";
+my $kdc_log     = "${TestLib::tmp_check}/krb5kdc.log";
+my $kdc_port    = int(rand() * 16384) + 49152;
+my $kdc_datadir = "${TestLib::tmp_check}/krb5kdc";
+my $kdc_pidfile = "${TestLib::tmp_check}/krb5kdc.pid";
+my $keytab      = "${TestLib::tmp_check}/krb5.keytab";
+
+note "setting up Kerberos";
+
+my ($stdout, $krb5_version);
+run_log [ $krb5_config, '--version' ], '>', \$stdout
+  or BAIL_OUT("could not execute krb5-config");
+BAIL_OUT("Heimdal is not supported") if $stdout =~ m/heimdal/;
+$stdout =~ m/Kerberos 5 release ([0-9]+\.[0-9]+)/
+  or BAIL_OUT("could not get Kerberos version");
+$krb5_version = $1;
+
+append_to_file(
+	$krb5_conf,
+	qq![logging]
+default = FILE:$krb5_log
+kdc = FILE:$kdc_log
+
+[libdefaults]
+default_realm = $realm
+
+[realms]
+$realm = {
+    kdc = $hostaddr:$kdc_port
+}!);
+
+append_to_file(
+	$kdc_conf,
+	qq![kdcdefaults]
+!);
+
+# For new-enough versions of krb5, use the _listen settings rather
+# than the _ports settings so that we can bind to localhost only.
+if ($krb5_version >= 1.15)
+{
+	append_to_file(
+		$kdc_conf,
+		qq!kdc_listen = $hostaddr:$kdc_port
+kdc_tcp_listen = $hostaddr:$kdc_port
+!);
+}
+else
+{
+	append_to_file(
+		$kdc_conf,
+		qq!kdc_ports = $kdc_port
+kdc_tcp_ports = $kdc_port
+!);
+}
+append_to_file(
+	$kdc_conf,
+	qq!
+[realms]
+$realm = {
+    database_name = $kdc_datadir/principal
+    admin_keytab = FILE:$kdc_datadir/kadm5.keytab
+    acl_file = $kdc_datadir/kadm5.acl
+    key_stash_file = $kdc_datadir/_k5.$realm
+}!);
+
+remove_tree $kdc_datadir;
+mkdir $kdc_datadir or die;
+
+$ENV{'KRB5_CONFIG'}      = $krb5_conf;
+$ENV{'KRB5_KDC_PROFILE'} = $kdc_conf;
+
+my $service_principal = "$ENV{with_krb_srvnam}/$host";
+
+system_or_bail $kdb5_util, 'create', '-s', '-P', 'secret0';
+
+my $test1_password = 'secret1';
+system_or_bail $kadmin_local, '-q', "addprinc -pw $test1_password test1";
+
+system_or_bail $kadmin_local, '-q', "addprinc -randkey $service_principal";
+system_or_bail $kadmin_local, '-q', "ktadd -k $keytab $service_principal";
+
+system_or_bail $krb5kdc, '-P', $kdc_pidfile;
+
+END
+{
+	kill 'INT', `cat $kdc_pidfile` if -f $kdc_pidfile;
+}
+
+note "setting up PostgreSQL instance";
+
+my $node = get_new_node('node');
+$node->init;
+$node->append_conf('postgresql.conf', "listen_addresses = 'localhost'");
+$node->append_conf('postgresql.conf', "krb_server_keyfile = '$keytab'");
+$node->start;
+
+$node->safe_psql('postgres', 'CREATE USER test1;');
+
+note "running tests";
+
+sub test_access
+{
+	my ($node, $gssencmode, $expected_res, $test_name) = @_;
+
+	my $res = $node->psql(
+		"postgres",
+		"SELECT 1",
+		extra_params => [
+			"-d",
+			$node->connstr("postgres") . " host=$host hostaddr=$hostaddr gssencmode=$gssencmode",
+			"-U", "test1",
+		]);
+	is($res, $expected_res, $test_name);
+	return;
+}
+
+unlink($node->data_dir . "/pg_ident.conf");
+$node->append_conf("pg_ident.conf", 'mymap /^(.*)@EXAMPLE.COM$ \1');
+run_log [ $kinit, 'test1' ], \$test1_password or BAIL_OUT($?);
+
+unlink($node->data_dir . '/pg_hba.conf');
+$node->append_conf('pg_hba.conf',
+				   qq{hostgssenc all all $hostaddr/32 gss map=mymap});
+$node->restart;
+test_access($node, "require", 0, "GSS-encrypted access");
+test_access($node, "disable", 2, "GSS encryption disabled");
+
+unlink($node->data_dir . "/pg_hba.conf");
+$node->append_conf("pg_hba.conf", qq{hostgssenc all all $hostaddr/32 trust});
+$node->restart;
+test_access($node, "require", 0, "GSS encryption without auth");
+
+unlink($node->data_dir . "/pg_hba.conf");
+$node->append_conf("pg_hba.conf",
+				   qq{hostnogssenc all all localhost gss map=mymap});
+$node->restart;
+test_access($node, "prefer", 0, "GSS unencrypted fallback");
+
+# Check that the client can prevent fallback.
+test_access($node, "require", 2, "GSS unencrypted fallback prevention");
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index dae772d33a2206e0715a81c6144850cc032e9439..bf7fca54ee42597a4ae5a34173a38c900bf3dbc3 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1739,7 +1739,7 @@ pg_stat_activity| SELECT s.datid,
     s.backend_xmin,
     s.query,
     s.backend_type
-   FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, sslcompression, ssl_client_dn, ssl_client_serial, ssl_issuer_dn)
+   FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, sslcompression, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc)
      LEFT JOIN pg_database d ON ((s.datid = d.oid)))
      LEFT JOIN pg_authid u ON ((s.usesysid = u.oid)));
 pg_stat_all_indexes| SELECT c.oid AS relid,
@@ -1830,6 +1830,11 @@ pg_stat_database_conflicts| SELECT d.oid AS datid,
     pg_stat_get_db_conflict_bufferpin(d.oid) AS confl_bufferpin,
     pg_stat_get_db_conflict_startup_deadlock(d.oid) AS confl_deadlock
    FROM pg_database d;
+pg_stat_gssapi| SELECT s.pid,
+    s.gss_auth AS gss_authenticated,
+    s.gss_princ AS principal,
+    s.gss_enc AS encrypted
+   FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, sslcompression, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc);
 pg_stat_progress_cluster| SELECT s.pid,
     s.datid,
     d.datname,
@@ -1926,7 +1931,7 @@ pg_stat_replication| SELECT s.pid,
     w.sync_priority,
     w.sync_state,
     w.reply_time
-   FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, sslcompression, ssl_client_dn, ssl_client_serial, ssl_issuer_dn)
+   FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, sslcompression, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc)
      JOIN pg_stat_get_wal_senders() w(pid, state, sent_lsn, write_lsn, flush_lsn, replay_lsn, write_lag, flush_lag, replay_lag, sync_priority, sync_state, reply_time) ON ((s.pid = w.pid)))
      LEFT JOIN pg_authid u ON ((s.usesysid = u.oid)));
 pg_stat_ssl| SELECT s.pid,
@@ -1938,7 +1943,7 @@ pg_stat_ssl| SELECT s.pid,
     s.ssl_client_dn AS client_dn,
     s.ssl_client_serial AS client_serial,
     s.ssl_issuer_dn AS issuer_dn
-   FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, sslcompression, ssl_client_dn, ssl_client_serial, ssl_issuer_dn);
+   FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, sslcompression, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc);
 pg_stat_subscription| SELECT su.oid AS subid,
     su.subname,
     st.pid,
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index c0c6ff6751f46e37afef0aec9378be1d21fbff2b..f466df88477dda63861da61fb56af2596827a5a7 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -194,6 +194,11 @@ sub mkvcbuild
 		$postgres->RemoveFile('src/backend/libpq/be-secure-common.c');
 		$postgres->RemoveFile('src/backend/libpq/be-secure-openssl.c');
 	}
+	if (!$solution->{options}->{gss})
+	{
+		$postgres->RemoveFile('src/backend/libpq/be-gssapi-common.c');
+		$postgres->RemoveFile('src/backend/libpq/be-secure-gssapi.c');
+	}
 
 	my $snowball = $solution->AddProject('dict_snowball', 'dll', '',
 		'src/backend/snowball');
@@ -254,6 +259,11 @@ sub mkvcbuild
 		$libpq->RemoveFile('src/interfaces/libpq/fe-secure-common.c');
 		$libpq->RemoveFile('src/interfaces/libpq/fe-secure-openssl.c');
 	}
+	if (!$solution->{options}->{gss})
+	{
+		$libpq->RemoveFile('src/interfaces/libpq/fe-gssapi-common.c');
+		$libpq->RemoveFile('src/interfaces/libpq/fe-secure-gssapi.c');
+	}
 
 	my $libpqwalreceiver =
 	  $solution->AddProject('libpqwalreceiver', 'dll', '',