[PATCH v20] GSSAPI encryption support

Поиск
Список
Период
Сортировка
От Robbie Harwood
Тема [PATCH v20] GSSAPI encryption support
Дата
Msg-id jlgy38mir81.fsf@redhat.com
обсуждение исходный текст
Ответ на [PATCH v1] GSSAPI encryption support  (Robbie Harwood <rharwood@redhat.com>)
Ответы Re: [PATCH v20] GSSAPI encryption support  (Stephen Frost <sfrost@snowman.net>)
Re: [PATCH v20] GSSAPI encryption support  (Andres Freund <andres@anarazel.de>)
Re: [PATCH v20] GSSAPI encryption support  (Peter Eisentraut <peter.eisentraut@2ndquadrant.com>)
Список pgsql-hackers
Hello friends,

Attached please find version 20 of the GSSAPI encryption support.  This
has been rebased onto master (thanks Stephen for calling out ab69ea9).
Other changes since v19 from Stephen's review:

- About 100 lines of new comments

- pgindent run over code (only the stuff I'm changing; it reports other
  problems on master, but if I understand correctly, that's not on me to
  fix here)

Thanks for the feedback!  And speaking of feedback, this patch is in the
"Needs Review" state so if people have additional feedback, that would
be appreciated.  I've CC'd people who I can remember reviewing before;
they should of course feel no obligation to review again if time
commitments/interests do not allow.

Thanks,
--Robbie
From 6915ae2507bf7910c5eecfbd0b84805531c16a07 Mon Sep 17 00:00:00 2001
From: Robbie Harwood <rharwood@redhat.com>
Date: Thu, 10 May 2018 16:12:03 -0400
Subject: [PATCH] libpq GSSAPI encryption support

On both the frontend and backend, prepare for GSSAPI encryption
support by moving common code for error handling into a separate file.
Fix a TODO for handling multiple status messages in the process.
Eliminate the OIDs, which have not been needed for some time.

Add frontend and backend encryption support functions.  Keep the
context initiation for authentication-only separate on both the
frontend and backend in order to avoid concerns about changing the
requested flags to include encryption support.

In postmaster, pull GSSAPI authorization checking into a shared
function.  Also share the initiator name between the encryption and
non-encryption codepaths.

Modify pqsecure_write() to take a non-const pointer.  The pointer will
not be modified, but this change (or a const-discarding cast, or a
malloc()+memcpy()) is necessary for GSSAPI due to const/struct
interactions in C.

For HBA, add "hostgss" and "hostnogss" entries that behave similarly
to their SSL counterparts.  "hostgss" requires either "gss", "trust",
or "reject" for its authentication.

Simiarly, add a "gssmode" parameter to libpq.  Supported values are
"disable", "require", and "prefer".  Notably, negotiation will only be
attempted if credentials can be acquired.  Move credential acquisition
into its own function to support this behavior.

Finally, add documentation for everything new, and update existing
documentation on connection security.

Thanks to Michael Paquier for the Windows fixes.
---
 doc/src/sgml/client-auth.sgml           |  75 ++++-
 doc/src/sgml/libpq.sgml                 |  57 +++-
 doc/src/sgml/runtime.sgml               |  77 ++++-
 src/backend/libpq/Makefile              |   4 +
 src/backend/libpq/auth.c                | 116 +++----
 src/backend/libpq/be-gssapi-common.c    |  74 +++++
 src/backend/libpq/be-gssapi-common.h    |  26 ++
 src/backend/libpq/be-secure-gssapi.c    | 374 ++++++++++++++++++++++
 src/backend/libpq/be-secure.c           |  16 +
 src/backend/libpq/hba.c                 |  51 ++-
 src/backend/postmaster/pgstat.c         |   3 +
 src/backend/postmaster/postmaster.c     |  69 ++++-
 src/include/libpq/hba.h                 |   4 +-
 src/include/libpq/libpq-be.h            |  13 +-
 src/include/libpq/libpq.h               |   3 +
 src/include/libpq/pqcomm.h              |   5 +-
 src/include/pgstat.h                    |   3 +-
 src/interfaces/libpq/Makefile           |   4 +
 src/interfaces/libpq/fe-auth.c          |  82 +----
 src/interfaces/libpq/fe-connect.c       | 241 ++++++++++++++-
 src/interfaces/libpq/fe-gssapi-common.c | 130 ++++++++
 src/interfaces/libpq/fe-gssapi-common.h |  23 ++
 src/interfaces/libpq/fe-secure-gssapi.c | 394 ++++++++++++++++++++++++
 src/interfaces/libpq/fe-secure.c        |  16 +-
 src/interfaces/libpq/libpq-fe.h         |   3 +-
 src/interfaces/libpq/libpq-int.h        |  30 +-
 src/tools/msvc/Mkvcbuild.pm             |  10 +
 27 files changed, 1707 insertions(+), 196 deletions(-)
 create mode 100644 src/backend/libpq/be-gssapi-common.c
 create mode 100644 src/backend/libpq/be-gssapi-common.h
 create mode 100644 src/backend/libpq/be-secure-gssapi.c
 create mode 100644 src/interfaces/libpq/fe-gssapi-common.c
 create mode 100644 src/interfaces/libpq/fe-gssapi-common.h
 create mode 100644 src/interfaces/libpq/fe-secure-gssapi.c

diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml
index c2114021c3..6f9f2b7560 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>
+hostgss    <replaceable>database</replaceable>  <replaceable>user</replaceable>  <replaceable>IP-address</replaceable>
<replaceable>IP-mask</replaceable>  <replaceable>auth-method</replaceable>
<optional><replaceable>auth-options</replaceable></optional>
+hostnogss  <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> or
+       non-<acronym>GSSAPI</acronym> connection attempts.
       </para>
      <note>
       <para>
@@ -176,6 +179,42 @@ hostnossl  <replaceable>database</replaceable>  <replaceable>user</replaceable>
      </listitem>
     </varlistentry>
 
+    <varlistentry>
+     <term><literal>hostgss</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>hostgss</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>hostnogss</literal></term>
+     <listitem>
+      <para>
+       This record type has the opposite behavior of <literal>hostgss</literal>;
+       it only matches connection attempts made over
+       TCP/IP that do not use <acronym>GSSAPI</acronym>.
+      </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>
@@ -696,15 +736,17 @@ 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
+# matched first), but allow GSSAPI-encrypted connections from anywhere else
+# on the Internet.  Unencrypted GSSAPI connections are allowed from
+# 192.168.12.10 only.  The zero mask causes no bits of the host IP
 # address to be considered, so it matches any host.
 #
 # TYPE  DATABASE        USER            ADDRESS                 METHOD
 host    all             all             192.168.54.1/32         reject
-host    all             all             0.0.0.0/0               gss
+hostgss 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
@@ -1051,13 +1093,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 as 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>hostgss</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 d2e5b08541..27896a2f35 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1316,6 +1316,61 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
       </listitem>
      </varlistentry>
 
+     <varlistentry id="libpq-connect-gssmode" xreflabel="gssmode">
+      <term><literal>gssmode</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
+           </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>gssmode</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 +8003,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/runtime.sgml b/doc/src/sgml/runtime.sgml
index 8d9d40664b..ac9c882d44 100644
--- a/doc/src/sgml/runtime.sgml
+++ b/doc/src/sgml/runtime.sgml
@@ -2008,9 +2008,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
@@ -2018,6 +2022,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>hostgss</literal> connections
+    (<xref linkend="auth-pg-hba-conf"/>) and use <literal>gss</literal>
+    authentication with them.  The TCP client must connect
+    using <literal>gssmode=require</literal>.
+  </para>
  </sect1>
 
  <sect1 id="encryption-options">
@@ -2114,8 +2126,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>hostgss</literal>).  Also, clients can specify that they
+      connect to servers only on GSSAPI-encrypted connections
+      (<literal>gssmode=require</literal>).
+     </para>
+
+     <para>
+      <application>Stunnel</application> or
+      <application>SSH</application> can also be used to encrypt
+      transmissions.
      </para>
     </listitem>
   </varlistentry>
@@ -2504,6 +2532,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 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/libpq/Makefile b/src/backend/libpq/Makefile
index 3dbec23e30..47efef0682 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 ff0832dba8..1027b0dae7 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -173,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 */
 
@@ -384,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 cannot be combined with non-GSSAPI authentication")));
+    }
+#endif
+
     /*
      * Now proceed to do the actual authentication check
      */
@@ -524,8 +532,13 @@ ClientAuthentication(Port *port)
 
         case uaGSS:
 #ifdef ENABLE_GSS
-            sendAuthRequest(port, AUTH_REQ_GSS, NULL, 0);
-            status = pg_GSS_recvauth(port);
+            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
@@ -1024,68 +1037,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)
 {
@@ -1094,7 +1045,6 @@ pg_GSS_recvauth(Port *port)
                 lmin_s,
                 gflags;
     int            mtype;
-    int            ret;
     StringInfoData buf;
     gss_buffer_desc gbuf;
 
@@ -1247,10 +1197,22 @@ 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;
+    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.
      */
@@ -1292,7 +1254,7 @@ pg_GSS_recvauth(Port *port)
                 elog(DEBUG2,
                      "GSSAPI realm (%s) and configured realm (%s) don't match",
                      cp, port->hba->krb_realm);
-                gss_release_buffer(&lmin_s, &gbuf);
+                gss_release_buffer(&min_stat, &gbuf);
                 return STATUS_ERROR;
             }
         }
@@ -1302,14 +1264,14 @@ pg_GSS_recvauth(Port *port)
         elog(DEBUG2,
              "GSSAPI did not return realm but realm matching was requested");
 
-        gss_release_buffer(&lmin_s, &gbuf);
+        gss_release_buffer(&min_stat, &gbuf);
         return STATUS_ERROR;
     }
 
     ret = check_usermap(port->hba->usermap, port->user_name, gbuf.value,
                         pg_krb_caseins_users);
 
-    gss_release_buffer(&lmin_s, &gbuf);
+    gss_release_buffer(&min_stat, &gbuf);
 
     return ret;
 }
@@ -2366,8 +2328,8 @@ InitializeLDAPConnection(Port *port, LDAP **ldap)
         char       *uris = NULL;
 
         /*
-         * We have a space-separated list of hostnames.  Convert it
-         * to a space-separated list of URIs.
+         * We have a space-separated list of hostnames.  Convert it to a
+         * space-separated list of URIs.
          */
         do
         {
diff --git a/src/backend/libpq/be-gssapi-common.c b/src/backend/libpq/be-gssapi-common.c
new file mode 100644
index 0000000000..40ada14bdd
--- /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 0000000000..f6e90ea3a7
--- /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 0000000000..e58efc37c3
--- /dev/null
+++ b/src/backend/libpq/be-secure-gssapi.c
@@ -0,0 +1,374 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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>
+
+/*
+ * Helper for sending data.  Assumes data is present to send.  Returns len
+ * when the buffer is empty; otherwise, it yields the underlying problem and
+ * sets errno.
+ */
+static ssize_t
+send_buffered_data(Port *port, size_t len)
+{
+    ssize_t        ret = secure_raw_write(
+                                       port,
+                                       port->gss->writebuf.data + port->gss->writebuf.cursor,
+                                       port->gss->writebuf.len - port->gss->writebuf.cursor);
+
+    if (ret < 0)
+        return ret;
+
+    /* update and possibly clear buffer state */
+    port->gss->writebuf.cursor += ret;
+
+    if (port->gss->writebuf.cursor == port->gss->writebuf.len)
+    {
+        /* entire request has now been written */
+        resetStringInfo(&port->gss->writebuf);
+        return len;
+    }
+
+    /* need to be called again */
+    errno = EWOULDBLOCK;
+    return -1;
+}
+
+/*
+ * Write len bytes of data from ptr along a GSSAPI-encrypted connection.
+ * Connection must be fully established (including authentication step) before
+ * calling.  Returns len once complete.  Data is internally buffered; in the
+ * case of an incomplete write, a negative value will be returned and errno
+ * 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)
+{
+    OM_uint32    major,
+                minor;
+    gss_buffer_desc input,
+                output;
+    ssize_t        ret = -1;
+    int            conf = 0;
+    uint32        netlen;
+    pg_gssinfo *gss = port->gss;
+
+    if (gss->writebuf.len != 0)
+        return send_buffered_data(port, len);
+
+    /* encrypt the message */
+    output.value = NULL;
+    output.length = 0;
+    input.value = ptr;
+    input.length = len;
+
+    major = gss_wrap(&minor, gss->ctx, 1, GSS_C_QOP_DEFAULT,
+                     &input, &conf, &output);
+    if (major != GSS_S_COMPLETE)
+    {
+        pg_GSS_error(ERROR, gettext_noop("GSSAPI wrap error"), major, minor);
+        goto cleanup;
+    }
+    else if (conf == 0)
+    {
+        ereport(FATAL, (errmsg("GSSAPI did not provide confidentiality")));
+        goto cleanup;
+    }
+
+    /* 4 network-order length bytes, then payload */
+    netlen = htonl(output.length);
+    appendBinaryStringInfo(&gss->writebuf, (char *) &netlen, 4);
+    appendBinaryStringInfo(&gss->writebuf, output.value, output.length);
+
+    ret = send_buffered_data(port, len);
+cleanup:
+    if (output.value != NULL)
+        gss_release_buffer(&minor, &output);
+    return ret;
+}
+
+/*
+ * Helper function to copy up to len bytes of data from the read buffer into
+ * ptr.  Returns number of bytes copied this way.
+ */
+static ssize_t
+read_from_buffer(pg_gssinfo *gss, void *ptr, size_t len)
+{
+    ssize_t        ret = 0;
+
+    /* load up any available data */
+    if (gss->buf.len > 4 && gss->buf.cursor < gss->buf.len)
+    {
+        /* clamp length */
+        if (len > gss->buf.len - gss->buf.cursor)
+            len = gss->buf.len - gss->buf.cursor;
+
+        memcpy(ptr, gss->buf.data + gss->buf.cursor, len);
+        gss->buf.cursor += len;
+        ret = len;
+    }
+
+    /* reset buffer if all data has been read */
+    if (gss->buf.cursor == gss->buf.len)
+        resetStringInfo(&gss->buf);
+
+    return ret;
+}
+
+/*
+ * Helper function to ensure the packet length (i.e., first four bytes of
+ * packet) have been read into the read buffer.  Returns 0 on success, or -1
+ * with errno set otherwise.  Read buffer is transparently resized if needed.
+ */
+static ssize_t
+load_packetlen(Port *port)
+{
+    pg_gssinfo *gss = port->gss;
+    ssize_t        ret;
+
+    if (gss->buf.len < 4)
+    {
+        enlargeStringInfo(&gss->buf, 4 - gss->buf.len);
+        ret = secure_raw_read(port, gss->buf.data + gss->buf.len,
+                              4 - gss->buf.len);
+        if (ret < 0)
+            return ret;
+
+        /* update buffer state */
+        gss->buf.len += ret;
+        gss->buf.data[gss->buf.len] = '\0';
+        if (gss->buf.len < 4)
+        {
+            /* partial read from secure_raw_read() */
+            errno = EWOULDBLOCK;
+            return -1;
+        }
+    }
+    return 0;
+}
+
+/*
+ * Helper function to read the entire packet (len+4 bytes) into the read
+ * buffer.  Returns 0 on success, or -1 with errno set otherwise.  Read buffer
+ * is transparently resized if needed; resizing accounts for problematically
+ * large size requests.
+ */
+static ssize_t
+load_packet(Port *port, size_t len)
+{
+    ssize_t        ret;
+    pg_gssinfo *gss = port->gss;
+
+    enlargeStringInfo(&gss->buf, len - gss->buf.len + 4);
+
+    ret = secure_raw_read(port, gss->buf.data + gss->buf.len,
+                          len - gss->buf.len + 4);
+    if (ret < 0)
+        return ret;
+
+    /* update buffer state */
+    gss->buf.len += ret;
+    gss->buf.data[gss->buf.len] = '\0';
+    if (gss->buf.len - 4 < len)
+    {
+        /* partial read from secure_raw_read() */
+        errno = EWOULDBLOCK;
+        return -1;
+    }
+    return 0;
+}
+
+/*
+ * 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, return -1 and set 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;
+    int            conf = 0;
+    pg_gssinfo *gss = port->gss;
+
+    if (gss->buf.cursor > 0)
+        return read_from_buffer(gss, ptr, len);
+
+    /* load length if not present */
+    ret = load_packetlen(port);
+    if (ret != 0)
+        return ret;
+
+    input.length = ntohl(*(uint32 *) gss->buf.data);
+    ret = load_packet(port, input.length);
+    if (ret != 0)
+        return ret;
+
+    /* decrypt the packet */
+    output.value = NULL;
+    output.length = 0;
+    input.value = gss->buf.data + 4;
+
+    major = gss_unwrap(&minor, gss->ctx, &input, &output, &conf, NULL);
+    if (major != GSS_S_COMPLETE)
+    {
+        pg_GSS_error(ERROR, gettext_noop("GSSAPI unwrap error"),
+                     major, minor);
+        ret = -1;
+        goto cleanup;
+    }
+    else if (conf == 0)
+    {
+        ereport(FATAL, (errmsg("GSSAPI did not provide confidentiality")));
+        ret = -1;
+        goto cleanup;
+    }
+
+    /* put the decrypted packet in the buffer */
+    resetStringInfo(&gss->buf);
+    enlargeStringInfo(&gss->buf, output.length);
+
+    memcpy(gss->buf.data, output.value, output.length);
+    gss->buf.len = output.length;
+    gss->buf.data[gss->buf.len] = '\0';
+
+    ret = read_from_buffer(gss, ptr, len);
+cleanup:
+    if (output.value != NULL)
+        gss_release_buffer(&minor, &output);
+    return ret;
+}
+
+/*
+ * 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.
+ */
+ssize_t
+secure_open_gssapi(Port *port)
+{
+    pg_gssinfo *gss = port->gss;
+    bool        complete_next = false;
+
+    /*
+     * 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)
+    {
+        OM_uint32    major,
+                    minor;
+        size_t        ret;
+        gss_buffer_desc input,
+                    output = GSS_C_EMPTY_BUFFER;
+
+        /* Handle any outgoing data */
+        if (gss->writebuf.len != 0)
+        {
+            ret = send_buffered_data(port, 1);
+            if (ret != 1)
+            {
+                WaitLatchOrSocket(MyLatch,
+                                  WL_SOCKET_WRITEABLE | WL_EXIT_ON_PM_DEATH,
+                                  port->sock, 0, WAIT_EVENT_GSS_OPEN_SERVER);
+                continue;
+            }
+        }
+
+        if (complete_next)
+            break;
+
+        /* Load incoming data */
+        ret = load_packetlen(port);
+        if (ret != 0)
+        {
+            WaitLatchOrSocket(MyLatch,
+                              WL_SOCKET_READABLE | WL_EXIT_ON_PM_DEATH,
+                              port->sock, 0, WAIT_EVENT_GSS_OPEN_SERVER);
+            continue;
+        }
+
+        input.length = ntohl(*(uint32 *) gss->buf.data);
+        ret = load_packet(port, input.length);
+        if (ret != 0)
+        {
+            WaitLatchOrSocket(MyLatch,
+                              WL_SOCKET_READABLE | WL_EXIT_ON_PM_DEATH,
+                              port->sock, 0, WAIT_EVENT_GSS_OPEN_SERVER);
+            continue;
+        }
+        input.value = gss->buf.data + 4;
+
+        /* 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);
+        resetStringInfo(&gss->buf);
+        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;
+        }
+
+        if (output.length != 0)
+        {
+            /* Queue packet for writing */
+            uint32        netlen = htonl(output.length);
+
+            appendBinaryStringInfo(&gss->writebuf, (char *) &netlen, 4);
+            appendBinaryStringInfo(&gss->writebuf,
+                                   output.value, output.length);
+            gss_release_buffer(&minor, &output);
+            continue;
+        }
+
+        /* We're done - woohoo! */
+        break;
+    }
+    port->gss->enc = true;
+    return 0;
+}
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index 3620b8ce8c..300fd0f071 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 0129dd24d0..943680dc5e 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, "hostgss") == 0 ||
+             strcmp(token->string, "hostnogss") == 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')    /* "hostgss" */
         {
-            parsedline->conntype = ctHostNoSSL;
+            parsedline->conntype = ctHostGSS;
+#ifndef ENABLE_GSS
+            ereport(elevel,
+                    (errcode(ERRCODE_CONFIG_FILE_ERROR),
+                     errmsg("hostgss 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 = "hostgss 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)
@@ -2060,6 +2088,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)
             {
@@ -2396,6 +2435,12 @@ fill_hba_line(Tuplestorestate *tuple_store, TupleDesc tupdesc,
             case ctHostNoSSL:
                 typestr = "hostnossl";
                 break;
+            case ctHostGSS:
+                typestr = "hostgss";
+                break;
+            case ctHostNoGSS:
+                typestr = "hostnogss";
+                break;
         }
         if (typestr)
             values[index++] = CStringGetTextDatum(typestr);
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 8676088e57..86514f7ed2 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -3558,6 +3558,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 eedc617db4..ad223bc681 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -1899,9 +1899,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;
@@ -1912,11 +1915,11 @@ ProcessStartupPacket(Port *port, bool SSLdone)
     if (pq_getbytes((char *) &len, 4) == EOF)
     {
         /*
-         * EOF after SSLdone probably means the client didn't like our
+         * EOF after secure_done probably means the client didn't like our
          * response to NEGOTIATE_SSL_CODE.  That's not an error condition, so
          * don't clutter the log with a complaint.
          */
-        if (!SSLdone)
+        if (!secure_done)
             ereport(COMMERROR,
                     (errcode(ERRCODE_PROTOCOL_VIOLATION),
                      errmsg("incomplete startup packet")));
@@ -1968,7 +1971,7 @@ ProcessStartupPacket(Port *port, bool SSLdone)
         return STATUS_ERROR;
     }
 
-    if (proto == NEGOTIATE_SSL_CODE && !SSLdone)
+    if (proto == NEGOTIATE_SSL_CODE && !secure_done)
     {
         char        SSLok;
 
@@ -2001,6 +2004,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 */
 
@@ -2435,6 +2464,18 @@ ConnCreate(int serverFd)
         ExitPostmaster(1);
     }
 #endif
+#ifdef ENABLE_GSS
+    {
+        MemoryContext save = CurrentMemoryContext;
+
+        CurrentMemoryContext = TopMemoryContext;
+
+        initStringInfo(&port->gss->buf);
+        initStringInfo(&port->gss->writebuf);
+
+        CurrentMemoryContext = save;
+    }
+#endif
 #endif
 
     return port;
@@ -2451,7 +2492,15 @@ ConnFree(Port *conn)
     secure_close(conn);
 #endif
     if (conn->gss)
+    {
+#ifdef ENABLE_GSS
+        if (conn->gss->buf.data)
+            pfree(conn->gss->buf.data);
+        if (conn->gss->writebuf.data)
+            pfree(conn->gss->writebuf.data);
+#endif
         free(conn->gss);
+    }
     free(conn);
 }
 
@@ -4790,6 +4839,18 @@ SubPostmasterMain(int argc, char *argv[])
                 (errcode(ERRCODE_OUT_OF_MEMORY),
                  errmsg("out of memory")));
 #endif
+#ifdef ENABLE_GSS
+    {
+        MemoryContext save = CurrentMemoryContext;
+
+        CurrentMemoryContext = TopMemoryContext;
+
+        initStringInfo(&port->gss->buf);
+        initStringInfo(&port->gss->writebuf);
+
+        CurrentMemoryContext = save;
+    }
+#endif
 
     /*
      * If appropriate, physically re-attach to shared memory segment. We want
diff --git a/src/include/libpq/hba.h b/src/include/libpq/hba.h
index 5f68f4c666..830ddaa25a 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 struct HbaLine
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index b2c59df54e..5d4ffee1e5 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -66,7 +66,7 @@ typedef struct
 #include "datatype/timestamp.h"
 #include "libpq/hba.h"
 #include "libpq/pqcomm.h"
-
+#include "lib/stringinfo.h"
 
 typedef enum CAC_state
 {
@@ -86,6 +86,9 @@ typedef struct
     gss_cred_id_t cred;            /* GSSAPI connection cred's */
     gss_ctx_id_t ctx;            /* GSSAPI connection context */
     gss_name_t    name;            /* GSSAPI client name */
+    bool        enc;            /* GSSAPI encryption in use */
+    StringInfoData buf;            /* GSSAPI encryption data buffering */
+    StringInfoData writebuf;    /* GSSAPI nonblocking write buffering */
 #endif
 } pg_gssinfo;
 #endif
@@ -275,7 +278,13 @@ extern void be_tls_get_peerdn_name(Port *port, char *ptr, size_t len);
 extern char *be_tls_get_certificate_hash(Port *port, size_t *len);
 #endif
 
-#endif    /* USE_SSL */
+#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;
 
diff --git a/src/include/libpq/libpq.h b/src/include/libpq/libpq.h
index e81752446e..7e5a8c7fac 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 cc0e0b32c7..ade1190096 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 f1c10d16b8..3771bc18df 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -787,7 +787,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;
 
 /* ----------
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index c2171d0856..238f208364 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/fe-auth.c b/src/interfaces/libpq/fe-auth.c
index 92641fe5e9..6875ad7f79 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
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index bc456fec0c..419a067b7a 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 gssmode similarly to sslmode - we can stil handle "disable" and
+     * "prefer".
+     */
+    {"gssmode", "PGGSSMODE", DefaultGSSMode, NULL,
+        "GSS-Mode", "", 7,        /* sizeof("disable") == 7 */
+    offsetof(struct pg_conn, gssmode)},
+
 #if defined(ENABLE_GSS) || defined(ENABLE_SSPI)
     /* Kerberos and GSSAPI authentication support specifying the service name */
     {"krbsrvname", "PGKRBSRVNAME", PG_KRB_SRVNAM, NULL,
@@ -1222,6 +1236,39 @@ connectOptions2(PGconn *conn)
             goto oom_error;
     }
 
+    /*
+     * validate gssmode option
+     */
+    if (conn->gssmode)
+    {
+        if (strcmp(conn->gssmode, "disable") != 0 &&
+            strcmp(conn->gssmode, "prefer") != 0 &&
+            strcmp(conn->gssmode, "require") != 0)
+        {
+            conn->status = CONNECTION_BAD;
+            printfPQExpBuffer(&conn->errorMessage,
+                              libpq_gettext("invalid gssmode value: \"%s\"\n"),
+                              conn->gssmode);
+            return false;
+        }
+#ifndef ENABLE_GSS
+        if (strcmp(conn->gssmode, "require") == 0)
+        {
+            conn->status = CONNECTION_BAD;
+            printfPQExpBuffer(
+                              &conn->errorMessage,
+                              libpq_gettext("no GSSAPI support; cannot require GSSAPI\n"));
+            return false;
+        }
+#endif
+    }
+    else
+    {
+        conn->gssmode = strdup(DefaultGSSMode);
+        if (!conn->gssmode)
+            goto oom_error;
+    }
+
     /*
      * Resolve special "auto" client_encoding from the locale
      */
@@ -1823,6 +1870,11 @@ connectDBStart(PGconn *conn)
      */
     resetPQExpBuffer(&conn->errorMessage);
 
+#ifdef ENABLE_GSS
+    if (conn->gssmode[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
@@ -2095,6 +2147,7 @@ PQconnectPoll(PGconn *conn)
         case CONNECTION_NEEDED:
         case CONNECTION_CHECK_WRITABLE:
         case CONNECTION_CONSUME:
+        case CONNECTION_GSS_STARTUP:
             break;
 
         default:
@@ -2324,6 +2377,7 @@ keep_going:                        /* We will come back to here until there is
                     getHostaddr(conn, host_addr, NI_MAXHOST);
                     if (strlen(host_addr) > 0)
                         conn->connip = strdup(host_addr);
+
                     /*
                      * purposely ignore strdup failure; not a big problem if
                      * it fails anyway.
@@ -2636,17 +2690,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 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 (conn->try_gss && !conn->gctx)
+                    conn->try_gss = pg_GSS_have_ccache(&conn->gcred);
+                if (conn->try_gss && !conn->gctx)
+                {
+                    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->gssmode[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 (IS_AF_UNIX(conn->raddr.addr.ss_family))
-                {
-                    /* Don't bother requesting SSL over a Unix socket */
-                    conn->allow_ssl_try = false;
-                }
                 if (conn->allow_ssl_try && !conn->wait_ssl_try &&
                     !conn->ssl_in_use)
                 {
@@ -2840,6 +2934,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->gssmode[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->gssmode[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.
@@ -2993,6 +3179,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 gssmode is "prefer" and we're using GSSAPI, retry
+                     * without it.
+                     */
+                    if (conn->gssenc && conn->gssmode[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
 
                     /*
@@ -3563,6 +3769,11 @@ makeEmptyPGconn(void)
     conn->verbosity = PQERRORS_DEFAULT;
     conn->show_context = PQSHOW_CONTEXT_ERRORS;
     conn->sock = PGINVALID_SOCKET;
+#ifdef ENABLE_GSS
+    conn->try_gss = true;
+    initPQExpBuffer(&conn->gbuf);
+    initPQExpBuffer(&conn->gwritebuf);
+#endif
 
     /*
      * We try to send at least 8K at a time, which is the usual size of pipe
@@ -3694,10 +3905,30 @@ freePGconn(PGconn *conn)
         free(conn->requirepeer);
     if (conn->connip)
         free(conn->connip);
+    if (conn->gssmode)
+        free(conn->gssmode);
 #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;
+    }
+    termPQExpBuffer(&conn->gbuf);
+    termPQExpBuffer(&conn->gwritebuf);
+#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 0000000000..3192f9190b
--- /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 0000000000..b429e79848
--- /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 0000000000..5d9c1f8f5b
--- /dev/null
+++ b/src/interfaces/libpq/fe-secure-gssapi.c
@@ -0,0 +1,394 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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"
+
+/*
+ * 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
+
+/*
+ * Helper function to write any pending data.  Returns len when it has all
+ * been written; otherwise, sets errno appropriately.  Assumes there is data
+ * to be written.
+ */
+static ssize_t
+send_buffered_data(PGconn *conn, size_t len)
+{
+    ssize_t        ret = pqsecure_raw_write(conn,
+                                         conn->gwritebuf.data + conn->gwritecurs,
+                                         conn->gwritebuf.len - conn->gwritecurs);
+
+    if (ret < 0)
+        return ret;
+
+    conn->gwritecurs += ret;
+
+    if (conn->gwritecurs == conn->gwritebuf.len)
+    {
+        /* entire request has now been written */
+        resetPQExpBuffer(&conn->gwritebuf);
+        conn->gwritecurs = 0;
+        return len;
+    }
+
+    /* need to be called again */
+    errno = EWOULDBLOCK;
+    return -1;
+}
+
+/*
+ * 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, void *ptr, size_t len)
+{
+    OM_uint32    major,
+                minor;
+    gss_buffer_desc input,
+                output = GSS_C_EMPTY_BUFFER;
+    ssize_t        ret = -1;
+    int            conf = 0;
+    uint32        netlen;
+
+    if (conn->gwritebuf.len != 0)
+        return send_buffered_data(conn, len);
+
+    /* encrypt the message */
+    input.value = ptr;
+    input.length = len;
+
+    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;
+    }
+
+    /* 4 network-order bytes of length, then payload */
+    netlen = htonl(output.length);
+    appendBinaryPQExpBuffer(&conn->gwritebuf, (char *) &netlen, 4);
+    appendBinaryPQExpBuffer(&conn->gwritebuf, output.value, output.length);
+
+    ret = send_buffered_data(conn, len);
+cleanup:
+    if (output.value != NULL)
+        gss_release_buffer(&minor, &output);
+    return ret;
+}
+
+/*
+ * Helper function which transfers up to len bytes from the read buffer into
+ * ptr.  Returns number of bytes transfered, and clears the read buffer when
+ * done.
+ */
+static ssize_t
+read_from_buffer(PGconn *conn, void *ptr, size_t len)
+{
+    ssize_t        ret = 0;
+
+    /* check for available data */
+    if (conn->gcursor < conn->gbuf.len)
+    {
+        /* clamp length */
+        if (len > conn->gbuf.len - conn->gcursor)
+            len = conn->gbuf.len - conn->gcursor;
+
+        memcpy(ptr, conn->gbuf.data + conn->gcursor, len);
+        conn->gcursor += len;
+        ret = len;
+    }
+
+    /* reset buffer if all data has been read */
+    if (conn->gcursor == conn->gbuf.len)
+    {
+        conn->gcursor = 0;
+        resetPQExpBuffer(&conn->gbuf);
+    }
+
+    return ret;
+}
+
+/*
+ * Ensure that the read buffer contains the packet length (4 bytes), reading
+ * more data and resizing the buffer where necessary.  Returns 0 when the
+ * length is present; otherwise, returns -1.  Retry if errno is EWOULDBLOCK or
+ * similar.
+ */
+static ssize_t
+load_packet_length(PGconn *conn)
+{
+    ssize_t        ret;
+
+    if (conn->gbuf.len < 4)
+    {
+        ret = enlargePQExpBuffer(&conn->gbuf, 4 - conn->gbuf.len);
+        if (ret != 1)
+        {
+            printfPQExpBuffer(&conn->errorMessage, libpq_gettext(
+                                                                 "Failed to fit packet length in buffer\n"));
+            return -1;
+        }
+
+        ret = pqsecure_raw_read(conn, conn->gbuf.data + conn->gbuf.len,
+                                4 - conn->gbuf.len);
+        if (ret < 0)
+            return ret;
+
+        /* update buffer state */
+        conn->gbuf.len += ret;
+        conn->gbuf.data[conn->gbuf.len] = '\0';
+        if (conn->gbuf.len < 4)
+        {
+            /* partial read from pqsecure_raw_read() */
+            errno = EWOULDBLOCK;
+            return -1;
+        }
+    }
+    return 0;
+}
+
+/*
+ * Read the entire packet (len+4 bytes) into the read buffer.  Returns 0 on
+ * success; otherwise, returns -1.  Retry if errno is EWOULDBLOCK or similar.
+ * Will resize the read buffer if needed; this will fail any maliciously large
+ * packets.
+ */
+static ssize_t
+load_packet(PGconn *conn, size_t len)
+{
+    ssize_t        ret;
+
+    ret = enlargePQExpBuffer(&conn->gbuf, len - conn->gbuf.len + 4);
+    if (ret != 1)
+    {
+        printfPQExpBuffer(&conn->errorMessage, libpq_gettext(
+                                                             "GSSAPI encrypted packet length %ld too big\n"),
+                          len);
+        return -1;
+    }
+
+    /* load any missing parts of the packet */
+    if (conn->gbuf.len - 4 < len)
+    {
+        ret = pqsecure_raw_read(conn, conn->gbuf.data + conn->gbuf.len,
+                                len - conn->gbuf.len + 4);
+        if (ret < 0)
+            return ret;
+
+        /* update buffer state */
+        conn->gbuf.len += ret;
+        conn->gbuf.data[conn->gbuf.len] = '\0';
+        if (conn->gbuf.len - 4 < len)
+        {
+            /* partial read from pqsecure_raw_read() */
+            errno = EWOULDBLOCK;
+            return -1;
+        }
+    }
+    return 0;
+}
+
+/*
+ * 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;
+    int            conf = 0;
+
+    /* handle any buffered data */
+    if (conn->gcursor != 0)
+        return read_from_buffer(conn, ptr, len);
+
+    /* load in the packet length, if not yet loaded */
+    ret = load_packet_length(conn);
+    if (ret < 0)
+        return ret;
+
+    input.length = ntohl(*(uint32 *) conn->gbuf.data);
+    ret = load_packet(conn, input.length);
+    if (ret < 0)
+        return ret;
+
+    /* decrypt the packet */
+    input.value = conn->gbuf.data + 4;
+
+    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;
+    }
+
+    /* load decrypted packet into our buffer */
+    conn->gcursor = 0;
+    resetPQExpBuffer(&conn->gbuf);
+    ret = enlargePQExpBuffer(&conn->gbuf, output.length);
+    if (ret != 1)
+    {
+        printfPQExpBuffer(&conn->errorMessage, libpq_gettext(
+                                                             "GSSAPI decrypted packet length %ld too big\n"),
+                          output.length);
+        ret = -1;
+        goto cleanup;
+    }
+
+    memcpy(conn->gbuf.data, output.value, output.length);
+    conn->gbuf.len = output.length;
+    conn->gbuf.data[conn->gbuf.len] = '\0';
+
+    ret = read_from_buffer(conn, ptr, len);
+cleanup:
+    if (output.value != NULL)
+        gss_release_buffer(&minor, &output);
+    return ret;
+}
+
+/*
+ * 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)
+{
+    ssize_t        ret;
+    OM_uint32    major,
+                minor;
+    uint32        netlen;
+    gss_buffer_desc input = GSS_C_EMPTY_BUFFER,
+                output = GSS_C_EMPTY_BUFFER;
+
+    /* Send out any data we might have */
+    if (conn->gwritebuf.len != 0)
+    {
+        ret = send_buffered_data(conn, 1);
+        if (ret < 0 && errno == EWOULDBLOCK)
+            return PGRES_POLLING_WRITING;
+        else if (ret == 1)
+            /* sent all data */
+            return PGRES_POLLING_READING;
+        return PGRES_POLLING_FAILED;
+    }
+
+    /* Client sends first, and sending creates a context */
+    if (conn->gctx)
+    {
+        /* Process any incoming data we might have */
+        ret = load_packet_length(conn);
+        if (ret < 0 && errno == EWOULDBLOCK)
+            return PGRES_POLLING_READING;
+        else if (ret < 0)
+            return PGRES_POLLING_FAILED;
+
+        if (conn->gbuf.data[0] == 'E')
+        {
+            /*
+             * We can taken an error here, and it's my least favorite thing.
+             * How long can error messages be?  That's a good question, but
+             * backend's pg_gss_error() caps them at 256.  Do a single read
+             * for that and call it a day.  Cope with this here rather than in
+             * load_packet since expandPQExpBuffer will destroy any data in
+             * the buffer on failure.
+             */
+            load_packet(conn, 256);
+            printfPQExpBuffer(&conn->errorMessage,
+                              libpq_gettext("Server error: %s"),
+                              conn->gbuf.data + 1);
+            return PGRES_POLLING_FAILED;
+        }
+
+        input.length = ntohl(*(uint32 *) conn->gbuf.data);
+        ret = load_packet(conn, input.length);
+        if (ret < 0 && errno == EWOULDBLOCK)
+            return PGRES_POLLING_READING;
+        else if (ret < 0)
+            return PGRES_POLLING_FAILED;
+
+        input.value = conn->gbuf.data + 4;
+    }
+
+    ret = pg_GSS_load_servicename(conn);
+    if (ret != STATUS_OK)
+        return PGRES_POLLING_FAILED;
+
+    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);
+    resetPQExpBuffer(&conn->gbuf);
+    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;
+        return PGRES_POLLING_OK;
+    }
+
+    /* Queue the token for writing */
+    netlen = htonl(output.length);
+    appendBinaryPQExpBuffer(&conn->gwritebuf, (char *) &netlen, 4);
+    appendBinaryPQExpBuffer(&conn->gwritebuf, output.value, output.length);
+    gss_release_buffer(&minor, &output);
+    return PGRES_POLLING_WRITING;
+}
diff --git a/src/interfaces/libpq/fe-secure.c b/src/interfaces/libpq/fe-secure.c
index a06fc7dc82..f4f196e3b4 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);
@@ -287,7 +294,7 @@ pqsecure_raw_read(PGconn *conn, void *ptr, size_t len)
  * to determine whether to continue/retry after error.
  */
 ssize_t
-pqsecure_write(PGconn *conn, const void *ptr, size_t len)
+pqsecure_write(PGconn *conn, void *ptr, size_t len)
 {
     ssize_t        n;
 
@@ -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);
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index 3f13ddf092..e8e0a9529a 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
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 66fd317b94..3315fde1ff 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -22,6 +22,7 @@
 
 /* We assume libpq-fe.h has already been included. */
 #include "libpq-events.h"
+#include "lib/stringinfo.h"
 
 #include <time.h>
 #ifndef WIN32
@@ -478,9 +479,19 @@ struct pg_conn
 #endif                            /* USE_OPENSSL */
 #endif                            /* USE_SSL */
 
+    char       *gssmode;        /* 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 */
+    PQExpBufferData gbuf;        /* GSS encryption buffering */
+    size_t        gcursor;        /* GSS buffering position */
+    PQExpBufferData gwritebuf;    /* GSS nonblocking write buffering */
+    size_t        gwritecurs;        /* GSS write buffer position */
+    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
@@ -656,7 +667,7 @@ extern void pqsecure_destroy(void);
 extern PostgresPollingStatusType pqsecure_open_client(PGconn *);
 extern void pqsecure_close(PGconn *);
 extern ssize_t pqsecure_read(PGconn *, void *ptr, size_t len);
-extern ssize_t pqsecure_write(PGconn *, const void *ptr, size_t len);
+extern ssize_t pqsecure_write(PGconn *, void *ptr, size_t len);
 extern ssize_t pqsecure_raw_read(PGconn *, void *ptr, size_t len);
 extern ssize_t pqsecure_raw_write(PGconn *, const void *ptr, size_t len);
 
@@ -748,6 +759,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, 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/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 2921d193a1..6ab6276cdb 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -190,6 +190,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');
@@ -250,6 +255,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', '',
-- 
2.19.2


Вложения

В списке pgsql-hackers по дате отправления:

Предыдущее
От: Robbie Harwood
Дата:
Сообщение: cfbot run pgindent?
Следующее
От: Alvaro Herrera
Дата:
Сообщение: Re: [HACKERS] CLUSTER command progress monitor