Re: [PATCH v3] GSSAPI encryption support - Mailing list pgsql-hackers

From Robbie Harwood
Subject Re: [PATCH v3] GSSAPI encryption support
Date
Msg-id jlgvbaao0sr.fsf@thriss.redhat.com
Whole thread Raw
In response to [PATCH v1] GSSAPI encryption support  (Robbie Harwood <rharwood@redhat.com>)
Responses Re: [PATCH v3] GSSAPI encryption support  (Michael Paquier <michael.paquier@gmail.com>)
Re: [PATCH v3] GSSAPI encryption support  (Craig Ringer <craig@2ndquadrant.com>)
List pgsql-hackers
Alright, here's v3.  As requested, it's one patch now.  Other things
addressed herein include:

 - postgres.h/assert.h ordering fix
 - spacing around casts
 - leaking of GSS buffer in be_gss_inplace_decrypt
 - libpq-be.h not having a conditional internal include
 - always exposing guc veriable gss_encrypt
 - copyright/description headers on all new files
 - movement of GSSAPI methods from fe-auth.c and auth.c to fe-gss.c and
   be-gss.c respectively
 - renaming GSSAPI files to fe-gss.c and be-gss.c (drops -secure)

Andres, one thing you mentioned as "feels rather wrong" was the
GSSAPI-specific code in pqcomm.c; while looking at that again, I have a
slightly better explanation than what I said previously.

Essentially, the problem is that socket_putmessage_noblock() needs to
know the size of the message to put in the buffer but we can't know
that until we've encrypted the message.  socket_putmessage_noblock()
calls socket_putmessage() after ensuring the call will not block;
however, other code paths simply call directly into socket_putmessage()
and so socket_putmessage() needs to have a path to encryption as well.

If you have other potential solutions to this problem, I would love to
hear them; right now though I don't see a better way.

Patch follows.  Thanks!
From 6710d5ad0226ea3a5ea8e35d6dc54b4500f1d3e0 Mon Sep 17 00:00:00 2001
From: "Robbie Harwood (frozencemetery)" <rharwood@redhat.com>
Date: Mon, 8 Jun 2015 19:27:45 -0400
Subject: [PATCH] GSSAPI encryption support

Encryption is opportuinistic by default for backward compatability, but can be
forced using a server HBA parameter or a client connection URI parameter.
---
 configure                           |   2 +
 configure.in                        |   1 +
 doc/src/sgml/client-auth.sgml       |  19 +-
 doc/src/sgml/libpq.sgml             |  12 ++
 doc/src/sgml/protocol.sgml          |  82 +++++++-
 doc/src/sgml/runtime.sgml           |  20 +-
 src/Makefile.global.in              |   1 +
 src/backend/libpq/Makefile          |   4 +
 src/backend/libpq/auth.c            | 338 +-----------------------------
 src/backend/libpq/be-gss.c          | 397 ++++++++++++++++++++++++++++++++++++
 src/backend/libpq/hba.c             |   9 +
 src/backend/libpq/pqcomm.c          |  39 ++++
 src/backend/tcop/postgres.c         |  30 ++-
 src/backend/utils/init/postinit.c   |   7 +-
 src/backend/utils/misc/guc.c        |  30 +++
 src/include/libpq/auth.h            |   2 +
 src/include/libpq/hba.h             |   1 +
 src/include/libpq/libpq-be.h        |  26 +++
 src/include/libpq/libpq.h           |   2 +
 src/interfaces/libpq/Makefile       |   4 +
 src/interfaces/libpq/fe-auth.c      | 182 -----------------
 src/interfaces/libpq/fe-connect.c   |  56 ++++-
 src/interfaces/libpq/fe-gss.c       | 280 +++++++++++++++++++++++++
 src/interfaces/libpq/fe-misc.c      |   5 +
 src/interfaces/libpq/fe-protocol3.c |  60 ++++++
 src/interfaces/libpq/libpq-int.h    |  16 ++
 26 files changed, 1097 insertions(+), 528 deletions(-)
 create mode 100644 src/backend/libpq/be-gss.c
 create mode 100644 src/interfaces/libpq/fe-gss.c

diff --git a/configure b/configure
index b771a83..a542577 100755
--- a/configure
+++ b/configure
@@ -712,6 +712,7 @@ with_uuid
 with_selinux
 with_openssl
 krb_srvtab
+with_gssapi
 with_python
 with_perl
 with_tcl
@@ -5488,6 +5489,7 @@ $as_echo "$with_gssapi" >&6; }



+
 #
 # Kerberos configuration parameters
 #
diff --git a/configure.in b/configure.in
index b5868b0..fccf542 100644
--- a/configure.in
+++ b/configure.in
@@ -636,6 +636,7 @@ PGAC_ARG_BOOL(with, gssapi, no, [build with GSSAPI support],
   krb_srvtab="FILE:\$(sysconfdir)/krb5.keytab"
 ])
 AC_MSG_RESULT([$with_gssapi])
+AC_SUBST(with_gssapi)


 AC_SUBST(krb_srvtab)
diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml
index 3b2935c..e6456a1 100644
--- a/doc/src/sgml/client-auth.sgml
+++ b/doc/src/sgml/client-auth.sgml
@@ -913,9 +913,10 @@ omicron         bryanh                  guest1
     <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.
+    that support it. The authentication itself is secure, and GSSAPI can be
+    used for connection encryption as well (see the
+    <literal>require_encrypt</literal> parameter below); <acronym>SSL</acronym>
+    can also be used for connection security.
    </para>

    <para>
@@ -1046,6 +1047,18 @@ omicron         bryanh                  guest1
        </para>
       </listitem>
      </varlistentry>
+
+     <varlistentry>
+      <term><literal>require_encrypt</literal></term>
+      <listitem>
+       <para>
+        Whether to require GSSAPI encryption.  Default is off, which causes
+        GSSAPI encryption to be enabled if available and requested for
+        compatibility with old clients.  It is recommended to set this unless
+        old clients are present.
+       </para>
+      </listitem>
+     </varlistentry>
     </variablelist>
    </para>
   </sect2>
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 0ee018e..32b4f1e 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1356,6 +1356,18 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
       </listitem>
      </varlistentry>

+     <varlistentry id="libpq-connect-gss-enc-require" xreflabel="gss-enc-require">
+      <term><literal>gss_enc_require</literal></term>
+      <listitem>
+       <para>
+        If set, whether to require GSSAPI encryption support from the remote
+        server. Defaults to unset, which will cause the client to fall back to
+        not using GSSAPI encryption if the server does not support encryption
+        through GSSAPI.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="libpq-connect-service" xreflabel="service">
       <term><literal>service</literal></term>
       <listitem>
diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml
index 34da859..cc65ee6 100644
--- a/doc/src/sgml/protocol.sgml
+++ b/doc/src/sgml/protocol.sgml
@@ -1295,6 +1295,42 @@
     of authentication checking.
    </para>
   </sect2>
+
+  <sect2>
+   <title><acronym>GSSAPI</acronym> Session Encryption</title>
+
+   <para>
+    If <productname>PostgreSQL</> was built with
+    <acronym>GSSAPI</acronym> and <acronym>GSSAPI</acronym> support, traffic
+    can also be encrypted using <acronym>GSSAPI</acronym>. To force encryption
+    using <acronym>GSSAPI</acronym>, set require_encrypt in
+    <filename>pg_hba.conf</filename>.
+   </para>
+
+   <para>
+    In order to probe for <acronym>GSSAPI</acronym> support, the client will
+    include in their StartupMessage the parameter gss_encrypt. If the server
+    does not support <acronym>GSSAPI</acronym> or <acronym>GSSAPI</acronym>
+    encryption, the server will error the connection; otherwise, it continues
+    as normal. The client may retry the connection
+    without <acronym>GSSAPI</acronym> encryption support depending on its
+    settings. If the client does not probe support, depending on settings
+    in <filename>pg_hba.conf</filename>, the server may drop
+    <acronym>GSSAPI</acronym>-authenticated connections without encryption.
+   </para>
+
+   <para>
+    If the client has probed <acronym>GSSAPI</acronym> encryption support and
+    the connection is <acronym>GSSAPI</acronym>-authenticated, then after the
+    server sends AuthenticationOk, all traffic between the client and server
+    will be <acronym>GSSAPI</acronym>-encrypted.  See message formats above.
+   </para>
+
+   <para>
+     It is valid to use <acronym>GSSAPI</acronym> encryption over
+     <acronym>SSL</acronym>-encrypted connections.
+   </para>
+  </sect2>
  </sect1>

 <sect1 id="protocol-replication">
@@ -2202,7 +2238,9 @@ Notice that although each message includes a byte count at the beginning,
 the message format is defined so that the message end can be found without
 reference to the byte count.  This aids validity checking.  (The CopyData
 message is an exception, because it forms part of a data stream; the contents
-of any individual CopyData message cannot be interpretable on their own.)
+of any individual CopyData message cannot be interpretable on their own.  The
+GSSAPI message type is another exception because GSSAPI does not require
+payloads to include length information.)
 </para>

 <variablelist>
@@ -3932,6 +3970,48 @@ FunctionCallResponse (B)
 </listitem>
 </varlistentry>

+<varlistentry>
+<term>
+GSSAPI (F & B)
+</term>
+<listitem>
+<para>
+<variablelist>
+<varlistentry>
+<term>
+        Byte1('g')
+</term>
+<listitem>
+<para>
+                Identifies the message as GSSAPI-encrypted data.
+</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>
+        Int32
+</term>
+<listitem>
+<para>
+                Length of message contents in bytes, including self.
+</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>
+        Byte<replaceable>n</replaceable>
+</term>
+<listitem>
+<para>
+                GSSAPI-encrypted data.  Once decrypted, will be in a valid
+                message format.
+</para>
+</listitem>
+</varlistentry>
+</variablelist>
+</para>
+</listitem>
+</varlistentry>

 <varlistentry>
 <term>
diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml
index 6d5b108..0d65fd6 100644
--- a/doc/src/sgml/runtime.sgml
+++ b/doc/src/sgml/runtime.sgml
@@ -1868,7 +1868,7 @@ pg_dumpall -p 5432 | psql -d postgres -p 5433
   </para>

   <para>
-   To prevent spoofing on TCP connections, the best solution is to use
+   To prevent spoofing on TCP connections, one possible solution is to use
    SSL certificates and make sure that clients check the server's certificate.
    To do that, the server
    must be configured to accept only <literal>hostssl</> connections (<xref
@@ -1878,6 +1878,15 @@ pg_dumpall -p 5432 | psql -d postgres -p 5433
    <literal>verify-full</> and have the appropriate root certificate
    file installed (<xref linkend="libq-ssl-certificates">).
   </para>
+
+  <para>
+   Another way of preventing spoofing on TCP connections is to use GSSAPI
+   encryption. In order to force all GSSAPI connections to be encrypted, one
+   should set <literal>require_encrypt</> in <filename>pg_hba.conf</> on GSS
+   connections. Then, using Kerberos, the client and server will mutually
+   authenticate, and the connection will be encrypted once the authentication
+   step is complete.
+  </para>
  </sect1>

  <sect1 id="encryption-options">
@@ -1993,6 +2002,15 @@ pg_dumpall -p 5432 | psql -d postgres -p 5433
       connect to servers only via SSL. <application>Stunnel</> or
       <application>SSH</> can also be used to encrypt transmissions.
      </para>
+
+     <para>
+      GSSAPI connections can also encrypt all data sent across the network. In
+      the <filename>pg_hba.conf</> file, the GSSAPI authentication method has
+      a parameter to require encryption; otherwise, connections will be
+      encrypted if available and requested by the client. On the client side,
+      there is also a parameter to require GSSAPI encryption support from the
+      server.
+     </para>
     </listitem>
   </varlistentry>

diff --git a/src/Makefile.global.in b/src/Makefile.global.in
index 51f4797..5814440 100644
--- a/src/Makefile.global.in
+++ b/src/Makefile.global.in
@@ -183,6 +183,7 @@ with_perl    = @with_perl@
 with_python    = @with_python@
 with_tcl    = @with_tcl@
 with_openssl    = @with_openssl@
+with_gssapi     = @with_gssapi@
 with_selinux    = @with_selinux@
 with_libxml    = @with_libxml@
 with_libxslt    = @with_libxslt@
diff --git a/src/backend/libpq/Makefile b/src/backend/libpq/Makefile
index 09410c4..ee69b82 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-gss.o
+endif
+
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index aca4ffe..83ff56f 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -36,7 +36,6 @@
  * Global authentication functions
  *----------------------------------------------------------------
  */
-static void sendAuthRequest(Port *port, AuthRequest areq);
 static void auth_failed(Port *port, int status, char *logdetail);
 static char *recv_password_packet(Port *port);
 static int    recv_and_check_password_packet(Port *port, char **logdetail);
@@ -122,30 +121,6 @@ static int    CheckLDAPAuth(Port *port);
 static int    CheckCertAuth(Port *port);
 #endif

-
-/*----------------------------------------------------------------
- * Kerberos and GSSAPI GUCs
- *----------------------------------------------------------------
- */
-char       *pg_krb_server_keyfile;
-bool        pg_krb_caseins_users;
-
-
-/*----------------------------------------------------------------
- * GSSAPI Authentication
- *----------------------------------------------------------------
- */
-#ifdef ENABLE_GSS
-#if defined(HAVE_GSSAPI_H)
-#include <gssapi.h>
-#else
-#include <gssapi/gssapi.h>
-#endif
-
-static int    pg_GSS_recvauth(Port *port);
-#endif   /* ENABLE_GSS */
-
-
 /*----------------------------------------------------------------
  * SSPI Authentication
  *----------------------------------------------------------------
@@ -166,23 +141,6 @@ static int    pg_SSPI_recvauth(Port *port);
 #endif
 static int    CheckRADIUSAuth(Port *port);

-
-/*
- * Maximum accepted size of GSS and SSPI authentication tokens.
- *
- * Kerberos tickets are usually quite small, but the TGTs issued by Windows
- * domain controllers include an authorization field known as the Privilege
- * Attribute Certificate (PAC), which contains the user's Windows permissions
- * (group memberships etc.). The PAC is copied into all tickets obtained on
- * the basis of this TGT (even those issued by Unix realms which the Windows
- * realm trusts), and can be several kB in size. The maximum token size
- * accepted by Windows systems is determined by the MaxAuthToken Windows
- * registry setting. Microsoft recommends that it is not set higher than
- * 65535 bytes, so that seems like a reasonable limit for us as well.
- */
-#define PG_MAX_AUTH_TOKEN_LENGTH    65535
-
-
 /*----------------------------------------------------------------
  * Global authentication functions
  *----------------------------------------------------------------
@@ -565,7 +523,7 @@ ClientAuthentication(Port *port)
 /*
  * Send an authentication request packet to the frontend.
  */
-static void
+void
 sendAuthRequest(Port *port, AuthRequest areq)
 {
     StringInfoData buf;
@@ -708,300 +666,6 @@ recv_and_check_password_packet(Port *port, char **logdetail)
     return result;
 }

-
-
-/*----------------------------------------------------------------
- * GSSAPI authentication system
- *----------------------------------------------------------------
- */
-#ifdef ENABLE_GSS
-
-#if defined(WIN32) && !defined(WIN32_ONLY_COMPILER)
-/*
- * MIT Kerberos GSSAPI DLL doesn't properly export the symbols for MingW
- * that contain the OIDs required. Redefine here, values copied
- * from src/athena/auth/krb5/src/lib/gssapi/generic/gssapi_generic.c
- */
-static const gss_OID_desc GSS_C_NT_USER_NAME_desc =
-{10, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x02"};
-static GSS_DLLIMP gss_OID GSS_C_NT_USER_NAME = &GSS_C_NT_USER_NAME_desc;
-#endif
-
-
-static void
-pg_GSS_error(int severity, char *errmsg, OM_uint32 maj_stat, OM_uint32 min_stat)
-{
-    gss_buffer_desc gmsg;
-    OM_uint32    lmin_s,
-                msg_ctx;
-    char        msg_major[128],
-                msg_minor[128];
-
-    /* Fetch major status message */
-    msg_ctx = 0;
-    gss_display_status(&lmin_s, maj_stat, GSS_C_GSS_CODE,
-                       GSS_C_NO_OID, &msg_ctx, &gmsg);
-    strlcpy(msg_major, gmsg.value, sizeof(msg_major));
-    gss_release_buffer(&lmin_s, &gmsg);
-
-    if (msg_ctx)
-
-        /*
-         * More than one message available. XXX: Should we loop and read all
-         * messages? (same below)
-         */
-        ereport(WARNING,
-                (errmsg_internal("incomplete GSS error report")));
-
-    /* Fetch mechanism minor status message */
-    msg_ctx = 0;
-    gss_display_status(&lmin_s, min_stat, GSS_C_MECH_CODE,
-                       GSS_C_NO_OID, &msg_ctx, &gmsg);
-    strlcpy(msg_minor, gmsg.value, sizeof(msg_minor));
-    gss_release_buffer(&lmin_s, &gmsg);
-
-    if (msg_ctx)
-        ereport(WARNING,
-                (errmsg_internal("incomplete GSS minor error report")));
-
-    /*
-     * errmsg_internal, since translation of the first part must be done
-     * before calling this function anyway.
-     */
-    ereport(severity,
-            (errmsg_internal("%s", errmsg),
-             errdetail_internal("%s: %s", msg_major, msg_minor)));
-}
-
-static int
-pg_GSS_recvauth(Port *port)
-{
-    OM_uint32    maj_stat,
-                min_stat,
-                lmin_s,
-                gflags;
-    int            mtype;
-    int            ret;
-    StringInfoData buf;
-    gss_buffer_desc gbuf;
-
-    /*
-     * GSS auth is not supported for protocol versions before 3, because it
-     * relies on the overall message length word to determine the GSS payload
-     * size in AuthenticationGSSContinue and PasswordMessage messages. (This
-     * is, in fact, a design error in our GSS support, because protocol
-     * messages are supposed to be parsable without relying on the length
-     * word; but it's not worth changing it now.)
-     */
-    if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
-        ereport(FATAL,
-                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                 errmsg("GSSAPI is not supported in protocol version 2")));
-
-    if (pg_krb_server_keyfile && strlen(pg_krb_server_keyfile) > 0)
-    {
-        /*
-         * Set default Kerberos keytab file for the Krb5 mechanism.
-         *
-         * setenv("KRB5_KTNAME", pg_krb_server_keyfile, 0); except setenv()
-         * not always available.
-         */
-        if (getenv("KRB5_KTNAME") == NULL)
-        {
-            size_t        kt_len = strlen(pg_krb_server_keyfile) + 14;
-            char       *kt_path = malloc(kt_len);
-
-            if (!kt_path ||
-                snprintf(kt_path, kt_len, "KRB5_KTNAME=%s",
-                         pg_krb_server_keyfile) != kt_len - 2 ||
-                putenv(kt_path) != 0)
-            {
-                ereport(LOG,
-                        (errcode(ERRCODE_OUT_OF_MEMORY),
-                         errmsg("out of memory")));
-                return STATUS_ERROR;
-            }
-        }
-    }
-
-    /*
-     * We accept any service principal that's present in our keytab. This
-     * increases interoperability between kerberos implementations that see
-     * for example case sensitivity differently, while not really opening up
-     * any vector of attack.
-     */
-    port->gss->cred = GSS_C_NO_CREDENTIAL;
-
-    /*
-     * Initialize sequence with an empty context
-     */
-    port->gss->ctx = GSS_C_NO_CONTEXT;
-
-    /*
-     * Loop through GSSAPI message exchange. This exchange can consist of
-     * multiple messags sent in both directions. First message is always from
-     * the client. All messages from client to server are password packets
-     * (type 'p').
-     */
-    do
-    {
-        pq_startmsgread();
-
-        CHECK_FOR_INTERRUPTS();
-
-        mtype = pq_getbyte();
-        if (mtype != 'p')
-        {
-            /* Only log error if client didn't disconnect. */
-            if (mtype != EOF)
-                ereport(COMMERROR,
-                        (errcode(ERRCODE_PROTOCOL_VIOLATION),
-                         errmsg("expected GSS response, got message type %d",
-                                mtype)));
-            return STATUS_ERROR;
-        }
-
-        /* Get the actual GSS token */
-        initStringInfo(&buf);
-        if (pq_getmessage(&buf, PG_MAX_AUTH_TOKEN_LENGTH))
-        {
-            /* EOF - pq_getmessage already logged error */
-            pfree(buf.data);
-            return STATUS_ERROR;
-        }
-
-        /* Map to GSSAPI style buffer */
-        gbuf.length = buf.len;
-        gbuf.value = buf.data;
-
-        elog(DEBUG4, "Processing received GSS token of length %u",
-             (unsigned int) gbuf.length);
-
-        maj_stat = gss_accept_sec_context(
-                                          &min_stat,
-                                          &port->gss->ctx,
-                                          port->gss->cred,
-                                          &gbuf,
-                                          GSS_C_NO_CHANNEL_BINDINGS,
-                                          &port->gss->name,
-                                          NULL,
-                                          &port->gss->outbuf,
-                                          &gflags,
-                                          NULL,
-                                          NULL);
-
-        /* gbuf no longer used */
-        pfree(buf.data);
-
-        elog(DEBUG5, "gss_accept_sec_context major: %d, "
-             "minor: %d, outlen: %u, outflags: %x",
-             maj_stat, min_stat,
-             (unsigned int) port->gss->outbuf.length, gflags);
-
-        CHECK_FOR_INTERRUPTS();
-
-        if (port->gss->outbuf.length != 0)
-        {
-            /*
-             * Negotiation generated data to be sent to the client.
-             */
-            elog(DEBUG4, "sending GSS response token of length %u",
-                 (unsigned int) port->gss->outbuf.length);
-
-            sendAuthRequest(port, AUTH_REQ_GSS_CONT);
-
-            gss_release_buffer(&lmin_s, &port->gss->outbuf);
-        }
-
-        if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
-        {
-            gss_delete_sec_context(&lmin_s, &port->gss->ctx, GSS_C_NO_BUFFER);
-            pg_GSS_error(ERROR,
-                       gettext_noop("accepting GSS security context failed"),
-                         maj_stat, min_stat);
-        }
-
-        if (maj_stat == GSS_S_CONTINUE_NEEDED)
-            elog(DEBUG4, "GSS continue needed");
-
-    } while (maj_stat == GSS_S_CONTINUE_NEEDED);
-
-    if (port->gss->cred != GSS_C_NO_CREDENTIAL)
-    {
-        /*
-         * Release service principal credentials
-         */
-        gss_release_cred(&min_stat, &port->gss->cred);
-    }
-
-    /*
-     * GSS_S_COMPLETE indicates that authentication is now complete.
-     *
-     * Get the name of the user that authenticated, and compare it to the pg
-     * username that was specified for the connection.
-     */
-    maj_stat = gss_display_name(&min_stat, port->gss->name, &gbuf, NULL);
-    if (maj_stat != GSS_S_COMPLETE)
-        pg_GSS_error(ERROR,
-                     gettext_noop("retrieving GSS user name failed"),
-                     maj_stat, min_stat);
-
-    /*
-     * Split the username at the realm separator
-     */
-    if (strchr(gbuf.value, '@'))
-    {
-        char       *cp = strchr(gbuf.value, '@');
-
-        /*
-         * If we are not going to include the realm in the username that is
-         * passed to the ident map, destructively modify it here to remove the
-         * realm. Then advance past the separator to check the realm.
-         */
-        if (!port->hba->include_realm)
-            *cp = '\0';
-        cp++;
-
-        if (port->hba->krb_realm != NULL && strlen(port->hba->krb_realm))
-        {
-            /*
-             * Match the realm part of the name first
-             */
-            if (pg_krb_caseins_users)
-                ret = pg_strcasecmp(port->hba->krb_realm, cp);
-            else
-                ret = strcmp(port->hba->krb_realm, cp);
-
-            if (ret)
-            {
-                /* GSS realm does not match */
-                elog(DEBUG2,
-                   "GSSAPI realm (%s) and configured realm (%s) don't match",
-                     cp, port->hba->krb_realm);
-                gss_release_buffer(&lmin_s, &gbuf);
-                return STATUS_ERROR;
-            }
-        }
-    }
-    else if (port->hba->krb_realm && strlen(port->hba->krb_realm))
-    {
-        elog(DEBUG2,
-             "GSSAPI did not return realm but realm matching was requested");
-
-        gss_release_buffer(&lmin_s, &gbuf);
-        return STATUS_ERROR;
-    }
-
-    ret = check_usermap(port->hba->usermap, port->user_name, gbuf.value,
-                        pg_krb_caseins_users);
-
-    gss_release_buffer(&lmin_s, &gbuf);
-
-    return ret;
-}
-#endif   /* ENABLE_GSS */
-
-
 /*----------------------------------------------------------------
  * SSPI authentication system
  *----------------------------------------------------------------
diff --git a/src/backend/libpq/be-gss.c b/src/backend/libpq/be-gss.c
new file mode 100644
index 0000000..2c3e58e
--- /dev/null
+++ b/src/backend/libpq/be-gss.c
@@ -0,0 +1,397 @@
+/*-------------------------------------------------------------------------
+ *
+ * be-gss.c
+ *      functions for GSSAPI support in the backend.
+ *
+ * Portions Copyright (c) 2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *      src/backend/libpq/be-gss.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "libpq/libpq.h"
+#include "libpq/auth.h"
+#include "miscadmin.h"
+
+#include <assert.h>
+
+#if defined(WIN32) && !defined(WIN32_ONLY_COMPILER)
+/*
+ * MIT Kerberos GSSAPI DLL doesn't properly export the symbols for MingW
+ * that contain the OIDs required. Redefine here, values copied
+ * from src/athena/auth/krb5/src/lib/gssapi/generic/gssapi_generic.c
+ */
+static const gss_OID_desc GSS_C_NT_USER_NAME_desc =
+{10, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x02"};
+static GSS_DLLIMP gss_OID GSS_C_NT_USER_NAME = &GSS_C_NT_USER_NAME_desc;
+#endif
+
+void
+pg_GSS_error(int severity, char *errmsg, OM_uint32 maj_stat, OM_uint32 min_stat)
+{
+    gss_buffer_desc gmsg;
+    OM_uint32    lmin_s,
+                msg_ctx;
+    char        msg_major[128],
+                msg_minor[128];
+
+    /* Fetch major status message */
+    msg_ctx = 0;
+    gss_display_status(&lmin_s, maj_stat, GSS_C_GSS_CODE,
+                       GSS_C_NO_OID, &msg_ctx, &gmsg);
+    strlcpy(msg_major, gmsg.value, sizeof(msg_major));
+    gss_release_buffer(&lmin_s, &gmsg);
+
+    if (msg_ctx)
+
+        /*
+         * More than one message available. XXX: Should we loop and read all
+         * messages? (same below)
+         */
+        ereport(WARNING,
+                (errmsg_internal("incomplete GSS error report")));
+
+    /* Fetch mechanism minor status message */
+    msg_ctx = 0;
+    gss_display_status(&lmin_s, min_stat, GSS_C_MECH_CODE,
+                       GSS_C_NO_OID, &msg_ctx, &gmsg);
+    strlcpy(msg_minor, gmsg.value, sizeof(msg_minor));
+    gss_release_buffer(&lmin_s, &gmsg);
+
+    if (msg_ctx)
+        ereport(WARNING,
+                (errmsg_internal("incomplete GSS minor error report")));
+
+    /*
+     * errmsg_internal, since translation of the first part must be done
+     * before calling this function anyway.
+     */
+    ereport(severity,
+            (errmsg_internal("%s", errmsg),
+             errdetail_internal("%s: %s", msg_major, msg_minor)));
+}
+
+int
+pg_GSS_recvauth(Port *port)
+{
+    OM_uint32    maj_stat,
+                min_stat,
+                lmin_s,
+                gflags;
+    int            mtype;
+    int            ret;
+    StringInfoData buf;
+    gss_buffer_desc gbuf;
+
+    /*
+     * GSS auth is not supported for protocol versions before 3, because it
+     * relies on the overall message length word to determine the GSS payload
+     * size in AuthenticationGSSContinue and PasswordMessage messages. (This
+     * is, in fact, a design error in our GSS support, because protocol
+     * messages are supposed to be parsable without relying on the length
+     * word; but it's not worth changing it now.)
+     */
+    if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
+        ereport(FATAL,
+                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                 errmsg("GSSAPI is not supported in protocol version 2")));
+
+    if (pg_krb_server_keyfile && strlen(pg_krb_server_keyfile) > 0)
+    {
+        /*
+         * Set default Kerberos keytab file for the Krb5 mechanism.
+         *
+         * setenv("KRB5_KTNAME", pg_krb_server_keyfile, 0); except setenv()
+         * not always available.
+         */
+        if (getenv("KRB5_KTNAME") == NULL)
+        {
+            size_t        kt_len = strlen(pg_krb_server_keyfile) + 14;
+            char       *kt_path = malloc(kt_len);
+
+            if (!kt_path ||
+                snprintf(kt_path, kt_len, "KRB5_KTNAME=%s",
+                         pg_krb_server_keyfile) != kt_len - 2 ||
+                putenv(kt_path) != 0)
+            {
+                ereport(LOG,
+                        (errcode(ERRCODE_OUT_OF_MEMORY),
+                         errmsg("out of memory")));
+                return STATUS_ERROR;
+            }
+        }
+    }
+
+    /*
+     * We accept any service principal that's present in our keytab. This
+     * increases interoperability between kerberos implementations that see
+     * for example case sensitivity differently, while not really opening up
+     * any vector of attack.
+     */
+    port->gss->cred = GSS_C_NO_CREDENTIAL;
+
+    /*
+     * Initialize sequence with an empty context
+     */
+    port->gss->ctx = GSS_C_NO_CONTEXT;
+
+    /*
+     * Loop through GSSAPI message exchange. This exchange can consist of
+     * multiple messags sent in both directions. First message is always from
+     * the client. All messages from client to server are password packets
+     * (type 'p').
+     */
+    do
+    {
+        pq_startmsgread();
+
+        CHECK_FOR_INTERRUPTS();
+
+        mtype = pq_getbyte();
+        if (mtype != 'p')
+        {
+            /* Only log error if client didn't disconnect. */
+            if (mtype != EOF)
+                ereport(COMMERROR,
+                        (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                         errmsg("expected GSS response, got message type %d",
+                                mtype)));
+            return STATUS_ERROR;
+        }
+
+        /* Get the actual GSS token */
+        initStringInfo(&buf);
+        if (pq_getmessage(&buf, PG_MAX_AUTH_TOKEN_LENGTH))
+        {
+            /* EOF - pq_getmessage already logged error */
+            pfree(buf.data);
+            return STATUS_ERROR;
+        }
+
+        /* Map to GSSAPI style buffer */
+        gbuf.length = buf.len;
+        gbuf.value = buf.data;
+
+        elog(DEBUG4, "Processing received GSS token of length %u",
+             (unsigned int) gbuf.length);
+
+        maj_stat = gss_accept_sec_context(
+                                          &min_stat,
+                                          &port->gss->ctx,
+                                          port->gss->cred,
+                                          &gbuf,
+                                          GSS_C_NO_CHANNEL_BINDINGS,
+                                          &port->gss->name,
+                                          NULL,
+                                          &port->gss->outbuf,
+                                          &gflags,
+                                          NULL,
+                                          NULL);
+
+        /* gbuf no longer used */
+        pfree(buf.data);
+
+        elog(DEBUG5, "gss_accept_sec_context major: %d, "
+             "minor: %d, outlen: %u, outflags: %x",
+             maj_stat, min_stat,
+             (unsigned int) port->gss->outbuf.length, gflags);
+
+        CHECK_FOR_INTERRUPTS();
+
+        if (port->gss->outbuf.length != 0)
+        {
+            /*
+             * Negotiation generated data to be sent to the client.
+             */
+            elog(DEBUG4, "sending GSS response token of length %u",
+                 (unsigned int) port->gss->outbuf.length);
+
+            sendAuthRequest(port, AUTH_REQ_GSS_CONT);
+
+            gss_release_buffer(&lmin_s, &port->gss->outbuf);
+        }
+
+        if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
+        {
+            gss_delete_sec_context(&lmin_s, &port->gss->ctx, GSS_C_NO_BUFFER);
+            pg_GSS_error(ERROR,
+                       gettext_noop("accepting GSS security context failed"),
+                         maj_stat, min_stat);
+        }
+
+        if (maj_stat == GSS_S_CONTINUE_NEEDED)
+            elog(DEBUG4, "GSS continue needed");
+
+    } while (maj_stat == GSS_S_CONTINUE_NEEDED);
+
+    if (port->gss->cred != GSS_C_NO_CREDENTIAL)
+    {
+        /*
+         * Release service principal credentials
+         */
+        gss_release_cred(&min_stat, &port->gss->cred);
+    }
+
+    /*
+     * GSS_S_COMPLETE indicates that authentication is now complete.
+     *
+     * Get the name of the user that authenticated, and compare it to the pg
+     * username that was specified for the connection.
+     */
+    maj_stat = gss_display_name(&min_stat, port->gss->name, &gbuf, NULL);
+    if (maj_stat != GSS_S_COMPLETE)
+        pg_GSS_error(ERROR,
+                     gettext_noop("retrieving GSS user name failed"),
+                     maj_stat, min_stat);
+
+    /*
+     * Split the username at the realm separator
+     */
+    if (strchr(gbuf.value, '@'))
+    {
+        char       *cp = strchr(gbuf.value, '@');
+
+        /*
+         * If we are not going to include the realm in the username that is
+         * passed to the ident map, destructively modify it here to remove the
+         * realm. Then advance past the separator to check the realm.
+         */
+        if (!port->hba->include_realm)
+            *cp = '\0';
+        cp++;
+
+        if (port->hba->krb_realm != NULL && strlen(port->hba->krb_realm))
+        {
+            /*
+             * Match the realm part of the name first
+             */
+            if (pg_krb_caseins_users)
+                ret = pg_strcasecmp(port->hba->krb_realm, cp);
+            else
+                ret = strcmp(port->hba->krb_realm, cp);
+
+            if (ret)
+            {
+                /* GSS realm does not match */
+                elog(DEBUG2,
+                   "GSSAPI realm (%s) and configured realm (%s) don't match",
+                     cp, port->hba->krb_realm);
+                gss_release_buffer(&lmin_s, &gbuf);
+                return STATUS_ERROR;
+            }
+        }
+    }
+    else if (port->hba->krb_realm && strlen(port->hba->krb_realm))
+    {
+        elog(DEBUG2,
+             "GSSAPI did not return realm but realm matching was requested");
+
+        gss_release_buffer(&lmin_s, &gbuf);
+        return STATUS_ERROR;
+    }
+
+    ret = check_usermap(port->hba->usermap, port->user_name, gbuf.value,
+                        pg_krb_caseins_users);
+
+    gss_release_buffer(&lmin_s, &gbuf);
+
+    return ret;
+}
+
+size_t
+be_gss_encrypt(Port *port, char msgtype, const char **msgptr, size_t len)
+{
+    OM_uint32 major, minor;
+    gss_buffer_desc input, output;
+    uint32 len_n;
+    int conf;
+    char *ptr = *((char **) msgptr);
+    char *newbuf = palloc(len + 5);
+
+    len += 4;
+    len_n = htonl(len);
+
+    newbuf[0] = msgtype;
+    memcpy(newbuf + 1, &len_n, 4);
+    memcpy(newbuf + 5, ptr, len - 4);
+
+    input.length = len + 1; /* include type */
+    input.value = newbuf;
+    output.length = 0;
+    output.value = NULL;
+
+    major = gss_wrap(&minor, port->gss->ctx, 1, GSS_C_QOP_DEFAULT, &input,
+                     &conf, &output);
+    if (GSS_ERROR(major))
+    {
+        pg_GSS_error(ERROR, gettext_noop("unwrapping GSS message failed"),
+                     major, minor);
+        return -1;
+    }
+    assert(conf);
+
+    newbuf = repalloc(newbuf, output.length);
+    memcpy(newbuf, output.value, output.length);
+
+    len = output.length;
+    *msgptr = newbuf;
+    gss_release_buffer(&minor, &output);
+
+    return len;
+}
+
+int
+be_gss_inplace_decrypt(StringInfo inBuf)
+{
+    OM_uint32 major, minor;
+    gss_buffer_desc input, output;
+    int qtype, conf;
+    size_t msglen = 0;
+
+    input.length = inBuf->len;
+    input.value = inBuf->data;
+    output.length = 0;
+    output.value = NULL;
+
+    major = gss_unwrap(&minor, MyProcPort->gss->ctx, &input, &output,
+                       &conf, NULL);
+    if (GSS_ERROR(major))
+    {
+        pg_GSS_error(ERROR, gettext_noop("wrapping GSS message failed"),
+                     major, minor);
+        return -1;
+    }
+    else if (conf == 0)
+    {
+        ereport(COMMERROR,
+                (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                 errmsg("Expected GSSAPI confidentiality but it was not received")));
+        gss_release_buffer(&minor, &output);
+        return -1;
+    }
+
+    qtype = ((char *)output.value)[0]; /* first byte is message type */
+    inBuf->len = output.length - 5; /* message starts */
+
+    memcpy((char *)&msglen, ((char *)output.value) + 1, 4);
+    msglen = ntohl(msglen);
+    if (msglen - 4 != inBuf->len)
+    {
+        ereport(COMMERROR,
+                (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                 errmsg("Length value inside GSSAPI-encrypted packet was malformed")));
+        gss_release_buffer(&minor, &output);
+        return -1;
+    }
+
+    memcpy(inBuf->data, ((char *)output.value) + 5, inBuf->len);
+    inBuf->data[inBuf->len] = '\0'; /* invariant */
+    gss_release_buffer(&minor, &output);
+
+    return qtype;
+}
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 23c8b5d..90fe57f 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -1570,6 +1570,15 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
         else
             hbaline->include_realm = false;
     }
+    else if (strcmp(name, "require_encrypt") == 0)
+    {
+        if (hbaline->auth_method != uaGSS)
+            INVALID_AUTH_OPTION("require_encrypt", "gssapi");
+        if (strcmp(val, "1") == 0)
+            hbaline->require_encrypt = true;
+        else
+            hbaline->require_encrypt = false;
+    }
     else if (strcmp(name, "radiusserver") == 0)
     {
         struct addrinfo *gai_result;
diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c
index 63673b1..2603951 100644
--- a/src/backend/libpq/pqcomm.c
+++ b/src/backend/libpq/pqcomm.c
@@ -1513,6 +1513,19 @@ socket_putmessage(char msgtype, const char *s, size_t len)
 {
     if (DoingCopyOut || PqCommBusy)
         return 0;
+
+#ifdef ENABLE_GSS
+    /* Do not wrap auth requests. */
+    if (MyProcPort->hba->auth_method == uaGSS && gss_encrypt &&
+        msgtype != 'R' && msgtype != 'g')
+    {
+        len = be_gss_encrypt(MyProcPort, msgtype, &s, len);
+        if (len < 0)
+            goto fail;
+        msgtype = 'g';
+    }
+#endif
+
     PqCommBusy = true;
     if (msgtype)
         if (internal_putbytes(&msgtype, 1))
@@ -1528,10 +1541,20 @@ socket_putmessage(char msgtype, const char *s, size_t len)
     if (internal_putbytes(s, len))
         goto fail;
     PqCommBusy = false;
+#ifdef ENABLE_GSS
+    /* if we're GSSAPI encrypting, s was allocated in be_gss_encrypt */
+    if (msgtype == 'g')
+        pfree((char *)s);
+#endif
     return 0;

 fail:
     PqCommBusy = false;
+#ifdef ENABLE_GSS
+    /* if we're GSSAPI encrypting, s was allocated in be_gss_encrypt */
+    if (msgtype == 'g')
+        pfree((char *)s);
+#endif
     return EOF;
 }

@@ -1547,6 +1570,22 @@ socket_putmessage_noblock(char msgtype, const char *s, size_t len)
     int res        PG_USED_FOR_ASSERTS_ONLY;
     int            required;

+#ifdef ENABLE_GSS
+    /*
+     * Because socket_putmessage is also a front-facing function, we need the
+     * ability to GSSAPI encrypt from either.  Since socket_putmessage_noblock
+     * calls into socket_putmessage, socket_putmessage will handle freeing the
+     * allocated string.
+     */
+    if (gss_encrypt && msgtype != 'R' && msgtype != 'g')
+    {
+        len = be_gss_encrypt(MyProcPort, msgtype, &s, len);
+        if (len < 0)
+            return;
+        msgtype = 'g';
+    }
+#endif
+
     /*
      * Ensure we have enough space in the output buffer for the message header
      * as well as the message itself.
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index d30fe35..0dc0376 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -336,6 +336,7 @@ static int
 SocketBackend(StringInfo inBuf)
 {
     int            qtype;
+    bool        msg_got = false;

     /*
      * Get message type code from the frontend.
@@ -365,6 +366,33 @@ SocketBackend(StringInfo inBuf)
         return qtype;
     }

+#ifdef ENABLE_GSS
+    else if (qtype == 'g' && gss_encrypt &&
+             MyProcPort->hba->auth_method == uaGSS)
+    {
+        /* GSSAPI wrapping implies protocol >= 3 */
+        if (pq_getmessage(inBuf, 0))
+            return EOF;
+        msg_got = true;
+
+        qtype = be_gss_inplace_decrypt(inBuf);
+        if (qtype < 0)
+            return EOF;
+    }
+    else if (gss_encrypt && MyProcPort->hba->auth_method == uaGSS &&
+             qtype != 'g' && qtype != 'R' )
+    {
+        /*
+         * Either something malicious is occuring, or we have lost
+         * synchronization.
+         */
+        ereport(FATAL,
+                (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                 errmsg("invalid frontend message type %d", qtype)));
+        return EOF;
+    }
+#endif
+
     /*
      * Validate message type code before trying to read body; if we have lost
      * sync, better to say "command unknown" than to run out of memory because
@@ -490,7 +518,7 @@ SocketBackend(StringInfo inBuf)
      * after the type code; we can read the message contents independently of
      * the type.
      */
-    if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
+    if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3 && !msg_got)
     {
         if (pq_getmessage(inBuf, 0))
             return EOF;            /* suitable message already logged */
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 7b19714..201c831 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -32,7 +32,7 @@
 #include "catalog/pg_db_role_setting.h"
 #include "catalog/pg_tablespace.h"
 #include "libpq/auth.h"
-#include "libpq/libpq-be.h"
+#include "libpq/libpq.h"
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
 #include "pgstat.h"
@@ -1087,6 +1087,11 @@ process_startup_options(Port *port, bool am_superuser)

         SetConfigOption(name, value, gucctx, PGC_S_CLIENT);
     }
+
+    if (!gss_encrypt && port->hba->require_encrypt)
+        ereport(FATAL, (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
+                        errmsg("GSSAPI encryption required from user \"%s\"",
+                               port->user_name)));
 }

 /*
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 71090f2..b422fd1 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -186,6 +186,8 @@ static const char *show_log_file_mode(void);
 static ConfigVariable *ProcessConfigFileInternal(GucContext context,
                           bool applySettings, int elevel);

+static void assign_gss_encrypt(bool newval, void *extra);
+static bool check_gss_encrypt(bool *newval, void **extra, GucSource source);

 /*
  * Options for enum values defined in this module.
@@ -477,6 +479,10 @@ static bool assert_enabled;
 /* should be static, but commands/variable.c needs to get at this */
 char       *role_string;

+/* Kerberos and GSSAPI GUCs */
+char       *pg_krb_server_keyfile;
+bool        pg_krb_caseins_users;
+bool        gss_encrypt;

 /*
  * Displayable names for context types (enum GucContext)
@@ -1610,6 +1616,15 @@ static struct config_bool ConfigureNamesBool[] =
         NULL, NULL, NULL
     },

+    {
+        {"gss_encrypt", PGC_USERSET, CONN_AUTH_SECURITY,
+         gettext_noop("Whether client wants encryption for this connection."),
+         NULL,
+         GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
+        },
+        &gss_encrypt, false, check_gss_encrypt, assign_gss_encrypt, NULL
+    },
+
     /* End-of-list marker */
     {
         {NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL, NULL
@@ -10108,4 +10123,19 @@ show_log_file_mode(void)
     return buf;
 }

+static void
+assign_gss_encrypt(bool newval, void *extra)
+{
+    gss_encrypt = newval;
+}
+
+static bool
+check_gss_encrypt(bool *newval, void **extra, GucSource source)
+{
+    if (MyProcPort && MyProcPort->hba && MyProcPort->hba->require_encrypt &&
+        !*newval)
+        return false;
+    return true;
+}
+
 #include "guc-file.c"
diff --git a/src/include/libpq/auth.h b/src/include/libpq/auth.h
index 80f26a8..ba197c6 100644
--- a/src/include/libpq/auth.h
+++ b/src/include/libpq/auth.h
@@ -26,4 +26,6 @@ extern void ClientAuthentication(Port *port);
 typedef void (*ClientAuthentication_hook_type) (Port *, int);
 extern PGDLLIMPORT ClientAuthentication_hook_type ClientAuthentication_hook;

+extern void sendAuthRequest(Port *port, AuthRequest areq);
+
 #endif   /* AUTH_H */
diff --git a/src/include/libpq/hba.h b/src/include/libpq/hba.h
index 68a953a..3435674 100644
--- a/src/include/libpq/hba.h
+++ b/src/include/libpq/hba.h
@@ -77,6 +77,7 @@ typedef struct HbaLine
     bool        clientcert;
     char       *krb_realm;
     bool        include_realm;
+    bool        require_encrypt;
     char       *radiusserver;
     char       *radiussecret;
     char       *radiusidentifier;
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index caaa8b5..98839a0 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -30,6 +30,7 @@
 #endif

 #ifdef ENABLE_GSS
+
 #if defined(HAVE_GSSAPI_H)
 #include <gssapi.h>
 #else
@@ -68,7 +69,22 @@ typedef struct
 #include "datatype/timestamp.h"
 #include "libpq/hba.h"
 #include "libpq/pqcomm.h"
+#include "lib/stringinfo.h"

+/*
+ * Maximum accepted size of GSS and SSPI authentication tokens.
+ *
+ * Kerberos tickets are usually quite small, but the TGTs issued by Windows
+ * domain controllers include an authorization field known as the Privilege
+ * Attribute Certificate (PAC), which contains the user's Windows permissions
+ * (group memberships etc.). The PAC is copied into all tickets obtained on
+ * the basis of this TGT (even those issued by Unix realms which the Windows
+ * realm trusts), and can be several kB in size. The maximum token size
+ * accepted by Windows systems is determined by the MaxAuthToken Windows
+ * registry setting. Microsoft recommends that it is not set higher than
+ * 65535 bytes, so that seems like a reasonable limit for us as well.
+ */
+#define PG_MAX_AUTH_TOKEN_LENGTH    65535

 typedef enum CAC_state
 {
@@ -214,6 +230,16 @@ extern void be_tls_get_cipher(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peerdn_name(Port *port, char *ptr, size_t len);
 #endif

+#ifdef ENABLE_GSS
+/* These functions are implemented in be-gss.c */
+extern int pg_GSS_recvauth(Port *port);
+extern size_t
+be_gss_encrypt(Port *port, char msgtype, const char **msgptr, size_t len);
+extern int be_gss_inplace_decrypt(StringInfo inBuf);
+extern void pg_GSS_error(int severity, char *errmsg, OM_uint32 maj_stat,
+                         OM_uint32 min_stat);
+#endif
+
 extern ProtocolVersion FrontendProtocol;

 /* TCP keepalives configuration. These are no-ops on an AF_UNIX socket. */
diff --git a/src/include/libpq/libpq.h b/src/include/libpq/libpq.h
index efb2dac..3fd6e7d 100644
--- a/src/include/libpq/libpq.h
+++ b/src/include/libpq/libpq.h
@@ -100,4 +100,6 @@ extern char *SSLCipherSuites;
 extern char *SSLECDHCurve;
 extern bool SSLPreferServerCiphers;

+extern bool gss_encrypt;
+
 #endif   /* LIBPQ_H */
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index c2105f1..807db20 100644
--- a/src/interfaces/libpq/Makefile
+++ b/src/interfaces/libpq/Makefile
@@ -48,6 +48,10 @@ ifeq ($(with_openssl),yes)
 OBJS += fe-secure-openssl.o
 endif

+ifeq ($(with_gssapi),yes)
+OBJS += fe-gss.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 5891c75..c606c90 100644
--- a/src/interfaces/libpq/fe-auth.c
+++ b/src/interfaces/libpq/fe-auth.c
@@ -42,188 +42,6 @@
 #include "fe-auth.h"
 #include "libpq/md5.h"

-
-#ifdef ENABLE_GSS
-/*
- * GSSAPI authentication system.
- */
-
-#if defined(WIN32) && !defined(WIN32_ONLY_COMPILER)
-/*
- * MIT Kerberos GSSAPI DLL doesn't properly export the symbols for MingW
- * that contain the OIDs required. Redefine here, values copied
- * from src/athena/auth/krb5/src/lib/gssapi/generic/gssapi_generic.c
- */
-static const gss_OID_desc GSS_C_NT_HOSTBASED_SERVICE_desc =
-{10, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04"};
-static GSS_DLLIMP gss_OID GSS_C_NT_HOSTBASED_SERVICE = &GSS_C_NT_HOSTBASED_SERVICE_desc;
-#endif
-
-/*
- * Fetch all errors of a specific type and append to "str".
- */
-static void
-pg_GSS_error_int(PQExpBuffer str, const char *mprefix,
-                 OM_uint32 stat, int type)
-{
-    OM_uint32    lmin_s;
-    gss_buffer_desc lmsg;
-    OM_uint32    msg_ctx = 0;
-
-    do
-    {
-        gss_display_status(&lmin_s, stat, type,
-                           GSS_C_NO_OID, &msg_ctx, &lmsg);
-        appendPQExpBuffer(str, "%s: %s\n", mprefix, (char *) lmsg.value);
-        gss_release_buffer(&lmin_s, &lmsg);
-    } while (msg_ctx);
-}
-
-/*
- * GSSAPI errors contain two parts; put both into conn->errorMessage.
- */
-static void
-pg_GSS_error(const char *mprefix, PGconn *conn,
-             OM_uint32 maj_stat, OM_uint32 min_stat)
-{
-    resetPQExpBuffer(&conn->errorMessage);
-
-    /* Fetch major error codes */
-    pg_GSS_error_int(&conn->errorMessage, mprefix, maj_stat, GSS_C_GSS_CODE);
-
-    /* Add the minor codes as well */
-    pg_GSS_error_int(&conn->errorMessage, mprefix, min_stat, GSS_C_MECH_CODE);
-}
-
-/*
- * Continue GSS authentication with next token as needed.
- */
-static int
-pg_GSS_continue(PGconn *conn)
-{
-    OM_uint32    maj_stat,
-                min_stat,
-                lmin_s;
-
-    maj_stat = gss_init_sec_context(&min_stat,
-                                    GSS_C_NO_CREDENTIAL,
-                                    &conn->gctx,
-                                    conn->gtarg_nam,
-                                    GSS_C_NO_OID,
-                                    GSS_C_MUTUAL_FLAG,
-                                    0,
-                                    GSS_C_NO_CHANNEL_BINDINGS,
-          (conn->gctx == GSS_C_NO_CONTEXT) ? GSS_C_NO_BUFFER : &conn->ginbuf,
-                                    NULL,
-                                    &conn->goutbuf,
-                                    NULL,
-                                    NULL);
-
-    if (conn->gctx != GSS_C_NO_CONTEXT)
-    {
-        free(conn->ginbuf.value);
-        conn->ginbuf.value = NULL;
-        conn->ginbuf.length = 0;
-    }
-
-    if (conn->goutbuf.length != 0)
-    {
-        /*
-         * GSS generated data to send to the server. We don't care if it's the
-         * first or subsequent packet, just send the same kind of password
-         * packet.
-         */
-        if (pqPacketSend(conn, 'p',
-                         conn->goutbuf.value, conn->goutbuf.length)
-            != STATUS_OK)
-        {
-            gss_release_buffer(&lmin_s, &conn->goutbuf);
-            return STATUS_ERROR;
-        }
-    }
-    gss_release_buffer(&lmin_s, &conn->goutbuf);
-
-    if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
-    {
-        pg_GSS_error(libpq_gettext("GSSAPI continuation error"),
-                     conn,
-                     maj_stat, min_stat);
-        gss_release_name(&lmin_s, &conn->gtarg_nam);
-        if (conn->gctx)
-            gss_delete_sec_context(&lmin_s, &conn->gctx, GSS_C_NO_BUFFER);
-        return STATUS_ERROR;
-    }
-
-    if (maj_stat == GSS_S_COMPLETE)
-        gss_release_name(&lmin_s, &conn->gtarg_nam);
-
-    return STATUS_OK;
-}
-
-/*
- * Send initial GSS authentication token
- */
-static int
-pg_GSS_startup(PGconn *conn)
-{
-    OM_uint32    maj_stat,
-                min_stat;
-    int            maxlen;
-    gss_buffer_desc temp_gbuf;
-
-    if (!(conn->pghost && conn->pghost[0] != '\0'))
-    {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("host name must be specified\n"));
-        return STATUS_ERROR;
-    }
-
-    if (conn->gctx)
-    {
-        printfPQExpBuffer(&conn->errorMessage,
-                    libpq_gettext("duplicate GSS authentication request\n"));
-        return STATUS_ERROR;
-    }
-
-    /*
-     * Import service principal name so the proper ticket can be acquired by
-     * the GSSAPI system.
-     */
-    maxlen = NI_MAXHOST + strlen(conn->krbsrvname) + 2;
-    temp_gbuf.value = (char *) malloc(maxlen);
-    if (!temp_gbuf.value)
-    {
-        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("out of memory\n"));
-        return STATUS_ERROR;
-    }
-    snprintf(temp_gbuf.value, maxlen, "%s@%s",
-             conn->krbsrvname, conn->pghost);
-    temp_gbuf.length = strlen(temp_gbuf.value);
-
-    maj_stat = gss_import_name(&min_stat, &temp_gbuf,
-                               GSS_C_NT_HOSTBASED_SERVICE, &conn->gtarg_nam);
-    free(temp_gbuf.value);
-
-    if (maj_stat != GSS_S_COMPLETE)
-    {
-        pg_GSS_error(libpq_gettext("GSSAPI name import error"),
-                     conn,
-                     maj_stat, min_stat);
-        return STATUS_ERROR;
-    }
-
-    /*
-     * Initial packet is the same as a continuation packet with no initial
-     * context.
-     */
-    conn->gctx = GSS_C_NO_CONTEXT;
-
-    return pg_GSS_continue(conn);
-}
-#endif   /* ENABLE_GSS */
-
-
 #ifdef ENABLE_SSPI
 /*
  * SSPI authentication system (Windows only)
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index f3030fb..4fcffa3 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -91,8 +91,9 @@ static int ldapServiceLookup(const char *purl, PQconninfoOption *options,
  * application_name in a startup packet.  We hard-wire the value rather
  * than looking into errcodes.h since it reflects historical behavior
  * rather than that of the current code.
+ * Servers that do not support GSS encryption will also return this error.
  */
-#define ERRCODE_APPNAME_UNKNOWN "42704"
+#define ERRCODE_UNKNOWN_PARAM "42704"

 /* This is part of the protocol so just define it */
 #define ERRCODE_INVALID_PASSWORD "28P01"
@@ -296,6 +297,12 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
     offsetof(struct pg_conn, gsslib)},
 #endif

+#if defined(ENABLE_GSS)
+    {"gss_enc_require", "GSS_ENC_REQUIRE", "0", NULL,
+        "Require-GSS-encryption", "", 1, /* should be '0' or '1' */
+    offsetof(struct pg_conn, gss_enc_require)},
+#endif
+
     {"replication", NULL, NULL, NULL,
         "Replication", "D", 5,
     offsetof(struct pg_conn, replication)},
@@ -2512,6 +2519,11 @@ keep_going:                        /* We will come back to here until there is
                     /* We are done with authentication exchange */
                     conn->status = CONNECTION_AUTH_OK;

+#ifdef ENABLE_GSS
+                    if (conn->gctx != 0)
+                        conn->gss_auth_done = true;
+#endif
+
                     /*
                      * Set asyncStatus so that PQgetResult will think that
                      * what comes back next is the result of a query.  See
@@ -2552,6 +2564,37 @@ keep_going:                        /* We will come back to here until there is
                     if (res->resultStatus != PGRES_FATAL_ERROR)
                         appendPQExpBufferStr(&conn->errorMessage,
                                              libpq_gettext("unexpected message from server during startup\n"));
+#ifdef ENABLE_GSS
+                    else if (!conn->gss_disable_enc &&
+                             *conn->gss_enc_require != '1')
+                    {
+                        /*
+                         * We tried to request GSS encryption, but the server
+                         * doesn't support it.  Retries are permitted here, so
+                         * hang up and try again.  A connection that doesn't
+                         * support appname will also not support GSSAPI
+                         * encryption, so this check goes before that check.
+                         * See comment below.
+                         */
+                        const char *sqlstate;
+
+                        sqlstate = PQresultErrorField(res, PG_DIAG_SQLSTATE);
+                        if (sqlstate &&
+                            strcmp(sqlstate, ERRCODE_UNKNOWN_PARAM) == 0)
+                        {
+                            OM_uint32 minor;
+
+                            PQclear(res);
+                            conn->gss_disable_enc = true;
+                            /* Must drop the old connection */
+                            pqDropConnection(conn);
+                            conn->status = CONNECTION_NEEDED;
+                            gss_delete_sec_context(&minor, &conn->gctx,
+                                                   GSS_C_NO_BUFFER);
+                            goto keep_going;
+                        }
+                    }
+#endif
                     else if (conn->send_appname &&
                              (conn->appname || conn->fbappname))
                     {
@@ -2569,7 +2612,7 @@ keep_going:                        /* We will come back to here until there is

                         sqlstate = PQresultErrorField(res, PG_DIAG_SQLSTATE);
                         if (sqlstate &&
-                            strcmp(sqlstate, ERRCODE_APPNAME_UNKNOWN) == 0)
+                            strcmp(sqlstate, ERRCODE_UNKNOWN_PARAM) == 0)
                         {
                             PQclear(res);
                             conn->send_appname = false;
@@ -2579,6 +2622,15 @@ keep_going:                        /* We will come back to here until there is
                             goto keep_going;
                         }
                     }
+#ifdef ENABLE_GSS
+                    else if (*conn->gss_enc_require == '1')
+                        /*
+                         * It has been determined that appname was not the
+                         * cause of connection failure, so give up.
+                         */
+                        appendPQExpBufferStr(&conn->errorMessage,
+                                             libpq_gettext("Server does not support required GSS encryption\n"));
+#endif

                     /*
                      * if the resultStatus is FATAL, then conn->errorMessage
diff --git a/src/interfaces/libpq/fe-gss.c b/src/interfaces/libpq/fe-gss.c
new file mode 100644
index 0000000..4ac52e5
--- /dev/null
+++ b/src/interfaces/libpq/fe-gss.c
@@ -0,0 +1,280 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-gss.c
+ *      functions for GSSAPI support in the frontend.
+ *
+ * Portions Copyright (c) 2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *      src/backend/libpq/fe-gss.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "libpq-fe.h"
+#include "postgres_fe.h"
+#include "fe-auth.h"
+#include "libpq-int.h"
+
+#include <assert.h>
+
+#if defined(WIN32) && !defined(WIN32_ONLY_COMPILER)
+/*
+ * MIT Kerberos GSSAPI DLL doesn't properly export the symbols for MingW
+ * that contain the OIDs required. Redefine here, values copied
+ * from src/athena/auth/krb5/src/lib/gssapi/generic/gssapi_generic.c
+ */
+static const gss_OID_desc GSS_C_NT_HOSTBASED_SERVICE_desc =
+{10, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04"};
+static GSS_DLLIMP gss_OID GSS_C_NT_HOSTBASED_SERVICE = &GSS_C_NT_HOSTBASED_SERVICE_desc;
+#endif
+
+/*
+ * Fetch all errors of a specific type and append to "str".
+ */
+static void
+pg_GSS_error_int(PQExpBuffer str, const char *mprefix,
+                 OM_uint32 stat, int type)
+{
+    OM_uint32    lmin_s;
+    gss_buffer_desc lmsg;
+    OM_uint32    msg_ctx = 0;
+
+    do
+    {
+        gss_display_status(&lmin_s, stat, type,
+                           GSS_C_NO_OID, &msg_ctx, &lmsg);
+        appendPQExpBuffer(str, "%s: %s\n", mprefix, (char *) lmsg.value);
+        gss_release_buffer(&lmin_s, &lmsg);
+    } while (msg_ctx);
+}
+
+/*
+ * GSSAPI errors contain two parts; put both into conn->errorMessage.
+ */
+void
+pg_GSS_error(const char *mprefix, PGconn *conn,
+             OM_uint32 maj_stat, OM_uint32 min_stat)
+{
+    resetPQExpBuffer(&conn->errorMessage);
+
+    /* Fetch major error codes */
+    pg_GSS_error_int(&conn->errorMessage, mprefix, maj_stat, GSS_C_GSS_CODE);
+
+    /* Add the minor codes as well */
+    pg_GSS_error_int(&conn->errorMessage, mprefix, min_stat, GSS_C_MECH_CODE);
+}
+
+/*
+ * Continue GSS authentication with next token as needed.
+ */
+int
+pg_GSS_continue(PGconn *conn)
+{
+    OM_uint32    maj_stat,
+                min_stat,
+                lmin_s;
+
+    maj_stat = gss_init_sec_context(&min_stat,
+                                    GSS_C_NO_CREDENTIAL,
+                                    &conn->gctx,
+                                    conn->gtarg_nam,
+                                    GSS_C_NO_OID,
+                                    GSS_C_MUTUAL_FLAG,
+                                    0,
+                                    GSS_C_NO_CHANNEL_BINDINGS,
+          (conn->gctx == GSS_C_NO_CONTEXT) ? GSS_C_NO_BUFFER : &conn->ginbuf,
+                                    NULL,
+                                    &conn->goutbuf,
+                                    NULL,
+                                    NULL);
+
+    if (conn->gctx != GSS_C_NO_CONTEXT)
+    {
+        free(conn->ginbuf.value);
+        conn->ginbuf.value = NULL;
+        conn->ginbuf.length = 0;
+    }
+
+    if (conn->goutbuf.length != 0)
+    {
+        /*
+         * GSS generated data to send to the server. We don't care if it's the
+         * first or subsequent packet, just send the same kind of password
+         * packet.
+         */
+        if (pqPacketSend(conn, 'p',
+                         conn->goutbuf.value, conn->goutbuf.length)
+            != STATUS_OK)
+        {
+            gss_release_buffer(&lmin_s, &conn->goutbuf);
+            return STATUS_ERROR;
+        }
+    }
+    gss_release_buffer(&lmin_s, &conn->goutbuf);
+
+    if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
+    {
+        pg_GSS_error(libpq_gettext("GSSAPI continuation error"),
+                     conn,
+                     maj_stat, min_stat);
+        gss_release_name(&lmin_s, &conn->gtarg_nam);
+        if (conn->gctx)
+            gss_delete_sec_context(&lmin_s, &conn->gctx, GSS_C_NO_BUFFER);
+        return STATUS_ERROR;
+    }
+
+    if (maj_stat == GSS_S_COMPLETE)
+        gss_release_name(&lmin_s, &conn->gtarg_nam);
+
+    return STATUS_OK;
+}
+
+/*
+ * Send initial GSS authentication token
+ */
+int
+pg_GSS_startup(PGconn *conn)
+{
+    OM_uint32    maj_stat,
+                min_stat;
+    int            maxlen;
+    gss_buffer_desc temp_gbuf;
+
+    if (!(conn->pghost && conn->pghost[0] != '\0'))
+    {
+        printfPQExpBuffer(&conn->errorMessage,
+                          libpq_gettext("host name must be specified\n"));
+        return STATUS_ERROR;
+    }
+
+    if (conn->gctx)
+    {
+        printfPQExpBuffer(&conn->errorMessage,
+                    libpq_gettext("duplicate GSS authentication request\n"));
+        return STATUS_ERROR;
+    }
+
+    /*
+     * Import service principal name so the proper ticket can be acquired by
+     * the GSSAPI system.
+     */
+    maxlen = NI_MAXHOST + strlen(conn->krbsrvname) + 2;
+    temp_gbuf.value = (char *) malloc(maxlen);
+    if (!temp_gbuf.value)
+    {
+        printfPQExpBuffer(&conn->errorMessage,
+                          libpq_gettext("out of memory\n"));
+        return STATUS_ERROR;
+    }
+    snprintf(temp_gbuf.value, maxlen, "%s@%s",
+             conn->krbsrvname, conn->pghost);
+    temp_gbuf.length = strlen(temp_gbuf.value);
+
+    maj_stat = gss_import_name(&min_stat, &temp_gbuf,
+                               GSS_C_NT_HOSTBASED_SERVICE, &conn->gtarg_nam);
+    free(temp_gbuf.value);
+
+    if (maj_stat != GSS_S_COMPLETE)
+    {
+        pg_GSS_error(libpq_gettext("GSSAPI name import error"),
+                     conn,
+                     maj_stat, min_stat);
+        return STATUS_ERROR;
+    }
+
+    /*
+     * Initial packet is the same as a continuation packet with no initial
+     * context.
+     */
+    conn->gctx = GSS_C_NO_CONTEXT;
+
+    return pg_GSS_continue(conn);
+}
+
+ssize_t
+pggss_inplace_decrypt(PGconn *conn, int gsslen)
+{
+    OM_uint32 major, minor;
+    gss_buffer_desc input, output;
+    ssize_t n;
+    int conf;
+
+    input.length = gsslen;
+    input.value = conn->inBuffer + conn->inCursor;
+    output.length = 0;
+    output.value = NULL;
+
+    major = gss_unwrap(&minor, conn->gctx, &input, &output, &conf, NULL);
+    if (GSS_ERROR(major))
+    {
+        pg_GSS_error("GSSAPI unwrap error", conn, major, minor);
+        return -1;
+    }
+    else if (conf == 0)
+    {
+        printfPQExpBuffer(&conn->errorMessage,
+                          libpq_gettext(
+                              "received GSSAPI message without confidentiality\n"));
+        return -1;
+    }
+
+    memcpy(conn->inBuffer + conn->inStart, output.value, output.length);
+    n = output.length;
+    gss_release_buffer(&minor, &output);
+    return n;
+}
+
+int
+pggss_encrypt(PGconn *conn)
+{
+    OM_uint32 major, minor;
+    gss_buffer_desc input, output;
+    int msgLen, conf;
+    uint32 len_n;
+
+    if (conn->gss_disable_enc || !conn->gctx || !conn->gss_auth_done)
+        return 0;
+    assert(conn->outMsgStart > 0);
+
+    /* We need to encrypt message type as well */
+    conn->outMsgStart -= 1;
+    msgLen = conn->outMsgEnd - conn->outMsgStart;
+
+    input.value = conn->outBuffer + conn->outMsgStart;
+    input.length = msgLen;
+    output.length = 0;
+    output.value = NULL;
+
+    major = gss_wrap(&minor, conn->gctx, 1, GSS_C_QOP_DEFAULT, &input, &conf,
+                     &output);
+    if (GSS_ERROR(major))
+    {
+        pg_GSS_error("GSSAPI wrap error", conn, major, minor);
+        return -1;
+    }
+    else if (conf == 0)
+    {
+        printfPQExpBuffer(&conn->errorMessage,
+                          libpq_gettext(
+                              "Failed to obtain confidentiality for outgoing GSSAPI message\n"));
+        return -1;
+    }
+
+    msgLen = output.length + 4;
+    if (pqCheckOutBufferSpace(conn->outMsgStart + msgLen + 1, conn))
+        return -1;
+
+    conn->outBuffer[conn->outMsgStart] = 'g'; /* GSSAPI message */
+
+    len_n = htonl(msgLen);
+    memcpy(conn->outBuffer + conn->outMsgStart + 1, &len_n, 4);
+
+    memcpy(conn->outBuffer + conn->outMsgStart + 1 + 4,
+           output.value, output.length);
+    conn->outMsgEnd = conn->outMsgStart + msgLen + 1;
+
+    gss_release_buffer(&minor, &output);
+    return msgLen + 1;
+}
diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c
index 0dbcf73..2379aff 100644
--- a/src/interfaces/libpq/fe-misc.c
+++ b/src/interfaces/libpq/fe-misc.c
@@ -604,6 +604,11 @@ pqPutMsgEnd(PGconn *conn)
         memcpy(conn->outBuffer + conn->outMsgStart, &msgLen, 4);
     }

+#ifdef ENABLE_GSS
+    if (pggss_encrypt(conn) < 0)
+        return EOF;
+#endif
+
     /* Make message eligible to send */
     conn->outCount = conn->outMsgEnd;

diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c
index 641804c..6127b91 100644
--- a/src/interfaces/libpq/fe-protocol3.c
+++ b/src/interfaces/libpq/fe-protocol3.c
@@ -129,6 +129,58 @@ pqParseInput3(PGconn *conn)
             return;
         }

+#ifdef ENABLE_GSS
+        /* We want to be ready in both IDLE and BUSY states for encryption */
+        if (id == 'g' && !conn->gss_disable_enc && conn->gctx)
+        {
+            ssize_t encEnd, next;
+
+            encEnd = pggss_inplace_decrypt(conn, msgLength);
+            if (encEnd <= 0)
+            {
+                /* error message placed by pggss_inplace_decrypt() */
+                pqSaveErrorResult(conn);
+                conn->asyncStatus = PGASYNC_READY;
+                pqDropConnection(conn);
+                conn->status = CONNECTION_BAD;
+                return;
+            }
+
+            /* shift contents of buffer to account for slack */
+            encEnd += conn->inStart;
+            next = conn->inStart + msgLength + 5;
+            memmove(conn->inBuffer + encEnd, conn->inBuffer + next,
+                    conn->inEnd - next);
+            conn->inEnd = (conn->inEnd - next) + encEnd;
+
+            conn->inCursor = conn->inStart;
+            (void) pqGetc(&id, conn);
+            (void) pqGetInt(&msgLength, 4, conn);
+            msgLength -= 4;
+            if (msgLength != encEnd - conn->inCursor)
+            {
+                /* This isn't a sync error because decrypt was successful */
+                printfPQExpBuffer(&conn->errorMessage,
+                                  libpq_gettext(
+                                      "server lied about message length: got message length %ld, but expected legnth
%d\n"),
+                                  encEnd - conn->inCursor, msgLength);
+                /* build an error result holding the error message */
+                pqSaveErrorResult(conn);
+                /* drop out of GetResult wait loop */
+                conn->asyncStatus = PGASYNC_READY;
+
+                pqDropConnection(conn);
+                /* No more connection to backend */
+                conn->status = CONNECTION_BAD;
+            }
+            conn->gss_decrypted_cur = true;
+        }
+        else if (!conn->gss_disable_enc && conn->gss_auth_done &&
+                 !conn->gss_decrypted_cur && id != 'E')
+            /* This could be a sync error, so let's handle it as such. */
+            handleSyncLoss(conn, id, msgLength);
+#endif
+
         /*
          * NOTIFY and NOTICE messages can happen in any state; always process
          * them right away.
@@ -415,6 +467,9 @@ pqParseInput3(PGconn *conn)
         {
             /* Normal case: parsing agrees with specified length */
             conn->inStart = conn->inCursor;
+#ifdef ENABLE_GSS
+            conn->gss_decrypted_cur = false;
+#endif
         }
         else
         {
@@ -2083,6 +2138,11 @@ build_startup_packet(const PGconn *conn, char *packet,
     if (conn->client_encoding_initial && conn->client_encoding_initial[0])
         ADD_STARTUP_OPTION("client_encoding", conn->client_encoding_initial);

+#ifdef ENABLE_GSS
+    if (!conn->gss_disable_enc)
+        ADD_STARTUP_OPTION("gss_encrypt", "on");
+#endif
+
     /* Add any environment-driven GUC settings needed */
     for (next_eo = options; next_eo->envName; next_eo++)
     {
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 04d056e..cca90c3 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -446,6 +446,10 @@ struct pg_conn
     gss_name_t    gtarg_nam;        /* GSS target name */
     gss_buffer_desc ginbuf;        /* GSS input token */
     gss_buffer_desc goutbuf;    /* GSS output token */
+    bool gss_disable_enc;        /* Does server recognize gss_encrypt? */
+    bool gss_auth_done;            /* Did we finish the AUTH step? */
+    bool gss_decrypted_cur;        /* Is first message in buffer decrypted? */
+    char *gss_enc_require;        /* Can we downgrade to plaintext? */
 #endif

 #ifdef ENABLE_SSPI
@@ -643,6 +647,18 @@ extern bool pgtls_read_pending(PGconn *conn);
 extern ssize_t pgtls_write(PGconn *conn, const void *ptr, size_t len);

 /*
+ * GSSAPI functions defined in fe-gss.c
+ */
+#ifdef ENABLE_GSS
+extern ssize_t pggss_inplace_decrypt(PGconn *conn, int gsslen);
+extern int pggss_encrypt(PGconn *conn);
+extern void pg_GSS_error(const char *mprefix, PGconn *conn, OM_uint32 maj_stat,
+                         OM_uint32 min_stat);
+extern int pg_GSS_startup(PGconn *conn);
+extern int pg_GSS_continue(PGconn *conn);
+#endif /* ENABLE_GSS */
+
+/*
  * this is so that we can check if a connection is non-blocking internally
  * without the overhead of a function call
  */
--
2.6.1


Attachment

pgsql-hackers by date:

Previous
From: Andres Freund
Date:
Subject: Re: Proposal: pg_confcheck - syntactic & semantic validation of postgresql configuration files
Next
From: Amir Rohan
Date:
Subject: Re: Proposal: pg_confcheck - syntactic & semantic validation of postgresql configuration files