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

From Robbie Harwood
Subject Re: [PATCH v10] GSSAPI encryption support
Date
Msg-id jlgpoua3syy.fsf@thriss.redhat.com
Whole thread Raw
In response to [PATCH v1] GSSAPI encryption support  (Robbie Harwood <rharwood@redhat.com>)
Responses Re: [PATCH v10] GSSAPI encryption support
List pgsql-hackers
Hello friends,

Here is another version of GSSAPI encryption support, both on this email
and on my github:
https://github.com/frozencemetery/postgres/tree/feature/gssencrypt10

This version is intended to address Michael's review:

- Fixed Windows build.  Thanks to Michael for patches.

- Fixed buffering of large replies on the serverside.  This should fix
  the traceback that was being seen.  The issue had to do with the
  difference between the server and client calling conventions for the
  _read and _write functions.

- gss_enc_require renamed to gss_require_encrypt.  Slightly longer, but
  half the stomach churn.

- Move gss_encrypt out of the GUCs and into connection-specific logic.
  Thanks to Tom Lane for pointing me in the right direction here.

- Replace writev() with two calls to _raw_write().  I'm not attached to
  this design; if someone has a preference for allocating a buffer and
  making a single write from that, I could be persuaded.  I don't know
  what the performance tradeoffs are.

- Typo fixes.  I need to figure out spellchecking in my editor.

- More use of <acronym>.

- Change _should_crypto() functions to return bool.  Also rename them to
  be the _should_encrypt functions.

- Error message cleanup.

Thanks!
From 945805d45e8021f92ad73518b3a74ac6bab89525 Mon Sep 17 00:00:00 2001
From: Robbie Harwood <rharwood@redhat.com>
Date: Fri, 26 Feb 2016 16:07:05 -0500
Subject: [PATCH 1/3] Move common GSSAPI code into its own files

On both the frontend and backend, prepare for GSSAPI encryption suport
by moving common code for error handling into a common file.  Other than
build-system changes, no code changes occur in this patch.

Thanks to Michael Paquier for the Windows fixes.
---
 configure                               |  2 +
 configure.in                            |  1 +
 src/Makefile.global.in                  |  1 +
 src/backend/libpq/Makefile              |  4 ++
 src/backend/libpq/auth.c                | 63 +---------------------------
 src/backend/libpq/be-gssapi-common.c    | 74 +++++++++++++++++++++++++++++++++
 src/include/libpq/be-gssapi-common.h    | 26 ++++++++++++
 src/interfaces/libpq/Makefile           |  4 ++
 src/interfaces/libpq/fe-auth.c          | 48 +--------------------
 src/interfaces/libpq/fe-gssapi-common.c | 63 ++++++++++++++++++++++++++++
 src/interfaces/libpq/fe-gssapi-common.h | 21 ++++++++++
 src/tools/msvc/Mkvcbuild.pm             | 18 ++++++--
 12 files changed, 212 insertions(+), 113 deletions(-)
 create mode 100644 src/backend/libpq/be-gssapi-common.c
 create mode 100644 src/include/libpq/be-gssapi-common.h
 create mode 100644 src/interfaces/libpq/fe-gssapi-common.c
 create mode 100644 src/interfaces/libpq/fe-gssapi-common.h

diff --git a/configure b/configure
index b3f3abe..a5bd629 100755
--- a/configure
+++ b/configure
@@ -713,6 +713,7 @@ with_systemd
 with_selinux
 with_openssl
 krb_srvtab
+with_gssapi
 with_python
 with_perl
 with_tcl
@@ -5491,6 +5492,7 @@ $as_echo "$with_gssapi" >&6; }



+
 #
 # Kerberos configuration parameters
 #
diff --git a/configure.in b/configure.in
index 0bd90d7..4fd8f05 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/src/Makefile.global.in b/src/Makefile.global.in
index e94d6a5..3dbc5c2 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_systemd    = @with_systemd@
 with_libxml    = @with_libxml@
diff --git a/src/backend/libpq/Makefile b/src/backend/libpq/Makefile
index 09410c4..a8cc9a0 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
+endif
+
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 57c2f48..73d493e 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -136,11 +136,7 @@ bool        pg_krb_caseins_users;
  *----------------------------------------------------------------
  */
 #ifdef ENABLE_GSS
-#if defined(HAVE_GSSAPI_H)
-#include <gssapi.h>
-#else
-#include <gssapi/gssapi.h>
-#endif
+#include "libpq/be-gssapi-common.h"

 static int    pg_GSS_recvauth(Port *port);
 #endif   /* ENABLE_GSS */
@@ -715,63 +711,6 @@ recv_and_check_password_packet(Port *port, char **logdetail)
  */
 #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)
 {
diff --git a/src/backend/libpq/be-gssapi-common.c b/src/backend/libpq/be-gssapi-common.c
new file mode 100644
index 0000000..dc27fa8
--- /dev/null
+++ b/src/backend/libpq/be-gssapi-common.c
@@ -0,0 +1,74 @@
+/*-------------------------------------------------------------------------
+ *
+ * be-gssapi-common.c
+ *      Common code for GSSAPI authentication & encryption
+ *
+ * Portions Copyright (c) 1996-2016, 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 "libpq/be-gssapi-common.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)));
+}
diff --git a/src/include/libpq/be-gssapi-common.h b/src/include/libpq/be-gssapi-common.h
new file mode 100644
index 0000000..eca5a9d
--- /dev/null
+++ b/src/include/libpq/be-gssapi-common.h
@@ -0,0 +1,26 @@
+/*-------------------------------------------------------------------------
+ *
+ * be-gssapi-common.h
+ *      Definitions for GSSAPI authentication & encryption handling
+ *
+ *
+ * Portions Copyright (c) 1996-2016, 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, char *errmsg, OM_uint32 maj_stat, OM_uint32 min_stat);
+
+#endif /* BE_GSSAPI_COMMON_H */
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index 1b292d2..0ea87b6 100644
--- a/src/interfaces/libpq/Makefile
+++ b/src/interfaces/libpq/Makefile
@@ -48,6 +48,10 @@ ifeq ($(with_openssl),yes)
 OBJS += fe-secure-openssl.o
 endif

+ifeq ($(with_gssapi),yes)
+OBJS += fe-gssapi-common.o
+endif
+
 ifeq ($(PORTNAME), cygwin)
 override shlib = cyg$(NAME)$(DLSUFFIX)
 endif
diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c
index cd863a5..56528f2 100644
--- a/src/interfaces/libpq/fe-auth.c
+++ b/src/interfaces/libpq/fe-auth.c
@@ -47,53 +47,7 @@
 /*
  * 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);
-}
+#include "fe-gssapi-common.h"

 /*
  * Continue GSS authentication with next token as needed.
diff --git a/src/interfaces/libpq/fe-gssapi-common.c b/src/interfaces/libpq/fe-gssapi-common.c
new file mode 100644
index 0000000..bc2c977
--- /dev/null
+++ b/src/interfaces/libpq/fe-gssapi-common.c
@@ -0,0 +1,63 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-gssapi-common.c
+ *       The front-end (client) GSSAPI common code
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *      src/interfaces/libpq/fe-gssapi-common.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "fe-auth.h"
+#include "fe-gssapi-common.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);
+}
diff --git a/src/interfaces/libpq/fe-gssapi-common.h b/src/interfaces/libpq/fe-gssapi-common.h
new file mode 100644
index 0000000..4b31371
--- /dev/null
+++ b/src/interfaces/libpq/fe-gssapi-common.h
@@ -0,0 +1,21 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-gssapi-common.h
+ *
+ *      Definitions for GSSAPI common routines
+ *
+ * Portions Copyright (c) 1996-2016, 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
+
+void pg_GSS_error(const char *mprefix, PGconn *conn,
+                  OM_uint32 maj_stat, OM_uint32 min_stat);
+
+#endif /* FE_GSSAPI_COMMON_H */
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index e4fb44e..2fa2509 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -155,12 +155,17 @@ sub mkvcbuild
     $postgres->AddLibrary('wldap32.lib') if ($solution->{options}->{ldap});
     $postgres->FullExportDLL('postgres.lib');

-   # The OBJS scraper doesn't know about ifdefs, so remove be-secure-openssl.c
-   # if building without OpenSSL
+    # The OBJS scraper doesn't know about ifdefs, so remove be-secure-openssl.c
+    # if building without OpenSSL, and be-gssapi-common.c when building with
+    # GSSAPI.
     if (!$solution->{options}->{openssl})
     {
         $postgres->RemoveFile('src/backend/libpq/be-secure-openssl.c');
     }
+    if (!$solution->{options}->{gss})
+    {
+        $postgres->RemoveFile('src/backend/libpq/be-gssapi-common.c');
+    }

     my $snowball = $solution->AddProject('dict_snowball', 'dll', '',
         'src/backend/snowball');
@@ -210,12 +215,17 @@ sub mkvcbuild
         'src/interfaces/libpq/libpq.rc');
     $libpq->AddReference($libpgport);

-   # The OBJS scraper doesn't know about ifdefs, so remove fe-secure-openssl.c
-   # if building without OpenSSL
+    # The OBJS scraper doesn't know about ifdefs, so remove fe-secure-openssl.c
+    # if building without OpenSSL, and fe-gssapi-common.c when building with
+    # GSSAPI
     if (!$solution->{options}->{openssl})
     {
         $libpq->RemoveFile('src/interfaces/libpq/fe-secure-openssl.c');
     }
+    if (!$solution->{options}->{gss})
+    {
+        $libpq->RemoveFile('src/interfaces/libpq/fe-gssapi-common.c');
+    }

     my $libpqwalreceiver =
       $solution->AddProject('libpqwalreceiver', 'dll', '',
--
2.8.0.rc3

From 2dbd5df611eb007121580d5e92d8d805520bc082 Mon Sep 17 00:00:00 2001
From: Robbie Harwood <rharwood@redhat.com>
Date: Tue, 1 Mar 2016 14:07:53 -0500
Subject: [PATCH 2/3] Connection encryption support for GSSAPI

Existing GSSAPI authentication code is extended to support connection
encryption.  Encryption begins immediately following AUTH_REQ_OK.

Fallback for talking to older clients is included, as are both client
and server controls for requiring encryption.
---
 doc/src/sgml/client-auth.sgml           |  14 +-
 doc/src/sgml/libpq.sgml                 |  12 ++
 doc/src/sgml/runtime.sgml               |  22 ++-
 src/backend/libpq/Makefile              |   2 +-
 src/backend/libpq/auth.c                |   8 +-
 src/backend/libpq/be-secure-gssapi.c    | 321 +++++++++++++++++++++++++++++++
 src/backend/libpq/be-secure.c           |  16 ++
 src/backend/libpq/hba.c                 |   9 +
 src/backend/postmaster/postmaster.c     |  16 ++
 src/backend/utils/init/postinit.c       |  15 +-
 src/backend/utils/misc/guc.c            |   2 -
 src/include/libpq/hba.h                 |   1 +
 src/include/libpq/libpq-be.h            |  10 +
 src/include/libpq/libpq.h               |   2 +
 src/interfaces/libpq/Makefile           |   2 +-
 src/interfaces/libpq/fe-connect.c       |  99 +++++++++-
 src/interfaces/libpq/fe-gssapi-common.c |  12 ++
 src/interfaces/libpq/fe-gssapi-common.h |   1 +
 src/interfaces/libpq/fe-protocol3.c     |   5 +
 src/interfaces/libpq/fe-secure-gssapi.c | 330 ++++++++++++++++++++++++++++++++
 src/interfaces/libpq/fe-secure.c        |  16 +-
 src/interfaces/libpq/libpq-int.h        |  19 +-
 src/tools/msvc/Mkvcbuild.pm             |   8 +-
 23 files changed, 924 insertions(+), 18 deletions(-)
 create mode 100644 src/backend/libpq/be-secure-gssapi.c
 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 3b2935c..f0d2348 100644
--- a/doc/src/sgml/client-auth.sgml
+++ b/doc/src/sgml/client-auth.sgml
@@ -915,7 +915,7 @@ omicron         bryanh                  guest1
     provides automatic authentication (single sign-on) for systems
     that support it. The authentication itself is secure, but the
     data sent over the database connection will be sent unencrypted unless
-    <acronym>SSL</acronym> is used.
+    <acronym>SSL</acronym> or <acronym>GSSAPI</acronym> encryption is in use.
    </para>

    <para>
@@ -1046,6 +1046,18 @@ omicron         bryanh                  guest1
        </para>
       </listitem>
      </varlistentry>
+
+     <varlistentry id="gssapi-require-encrypt" xreflabel="require_encrypt">
+      <term><literal>require_encrypt</literal></term>
+      <listitem>
+       <para>
+        Require <acronym>GSSAPI</acronym> encryption.  Defaults to off, which
+        enables <acronym>GSSAPI</acronym> encryption only when both available
+        and requested to maintain compatibility with old clients.  This
+        setting should be enabled 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 2328d8f..5a5dc8f 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-require_encrypt" xreflabel="gss_require_encrypt">
+      <term><literal>gss_require_encrypt</literal></term>
+      <listitem>
+       <para>
+        Require <acronym>GSSAPI</acronym> encryption support from the remote
+        server when set.  By default, clients will fall back to not using
+        <acronym>GSSAPI</acronym> encryption if the server does not support
+        encryption through <acronym>GSSAPI</acronym>.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="libpq-connect-service" xreflabel="service">
       <term><literal>service</literal></term>
       <listitem>
diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml
index c699f21..d5c2df0 100644
--- a/doc/src/sgml/runtime.sgml
+++ b/doc/src/sgml/runtime.sgml
@@ -1917,7 +1917,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
@@ -1927,6 +1927,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
+   GSSAPI connections.  Then the client and server will mutually authenticate,
+   and the connection will be encrypted once the client and server agree that
+   the authentication step is complete.
+  </para>
  </sect1>

  <sect1 id="encryption-options">
@@ -2042,6 +2051,17 @@ 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
+      called <xref linkend="gssapi-require-encrypt">; unless set, 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
+      called <xref linkend="libpq-connect-gss-require-encrypt">.
+     </para>
     </listitem>
   </varlistentry>

diff --git a/src/backend/libpq/Makefile b/src/backend/libpq/Makefile
index a8cc9a0..5fa43e4 100644
--- a/src/backend/libpq/Makefile
+++ b/src/backend/libpq/Makefile
@@ -22,7 +22,7 @@ OBJS += be-secure-openssl.o
 endif

 ifeq ($(with_gssapi),yes)
-OBJS += be-gssapi-common.o
+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 73d493e..94d95bd 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -596,10 +596,12 @@ sendAuthRequest(Port *port, AuthRequest areq)
     pq_endmessage(&buf);

     /*
-     * Flush message so client will see it, except for AUTH_REQ_OK, which need
-     * not be sent until we are ready for queries.
+     * In most cases, we do not need to send AUTH_REQ_OK until we are ready
+     * for queries.  However, if we are doing GSSAPI encryption, that request
+     * must go out immediately to ensure that all messages which follow the
+     * AUTH_REQ_OK are not grouped with it and can therefore be encrypted.
      */
-    if (areq != AUTH_REQ_OK)
+    if (areq != AUTH_REQ_OK || port->gss != NULL)
         pq_flush();

     CHECK_FOR_INTERRUPTS();
diff --git a/src/backend/libpq/be-secure-gssapi.c b/src/backend/libpq/be-secure-gssapi.c
new file mode 100644
index 0000000..a4d5f71
--- /dev/null
+++ b/src/backend/libpq/be-secure-gssapi.c
@@ -0,0 +1,321 @@
+/*-------------------------------------------------------------------------
+ *
+ * be-secure-gssapi.c
+ *  GSSAPI encryption support
+ *
+ * Portions Copyright (c) 2016, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ *  src/backend/libpq/be-secure-gssapi.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "libpq/be-gssapi-common.h"
+
+#include "libpq/libpq.h"
+#include "libpq/libpq-be.h"
+#include "libpq/pqformat.h"
+#include "miscadmin.h"
+
+/*
+ * Wrapper function indicating whether we are currently performing GSSAPI
+ * connection encryption.
+ *
+ * gss->encrypt is set when connection parameters are processed, which happens
+ * immediately after AUTH_REQ_OK is sent.
+ */
+static bool
+be_gssapi_should_encrypt(Port *port)
+{
+    if (port->gss->ctx == GSS_C_NO_CONTEXT)
+        return false;
+    return port->gss->encrypt;
+}
+
+/*
+ * Send a message along the connection, possibly encrypting using GSSAPI.
+ *
+ * If we are not encrypting at the moment, we send data plaintext and follow
+ * the calling conventions of secure_raw_write.  Otherwise, the following
+ * hold: Incomplete writes are buffered using a dedicated StringInfo in the
+ * port structure.  On failure, we return -1; on partial write, we return -1
+ * and set errno=EWOULDBLOCK since the translation between plaintext and
+ * encrypted is indeterminate; on completed write, we return the total number
+ * of bytes written including any buffering that occurred.  Behavior when
+ * called with a new pointer/length combination after an incomplete write is
+ * undefined.
+ */
+ssize_t
+be_gssapi_write(Port *port, void *ptr, size_t len)
+{
+    OM_uint32 major, minor;
+    gss_buffer_desc input, output;
+    ssize_t ret;
+    int conf;
+    uint32 netlen;
+    char lenbuf[4];
+
+    ret = be_gssapi_should_encrypt(port);
+
+    if (ret == -1)
+        return -1;
+    else if (ret == 0)
+        return secure_raw_write(port, ptr, len);
+
+    /* send any data we have buffered */
+    if (port->gss->writebuf.len != 0)
+    {
+        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)
+        {
+            port->gss->writebuf.len = port->gss->writebuf.cursor = 0;
+            port->gss->writebuf.data[0] = '\0';
+
+            /* the entire request has now been written */
+            return len;
+        }
+
+        /* need to be called again */
+        errno = EWOULDBLOCK;
+        return -1;
+    }
+
+    /* encrypt the message */
+    output.value = NULL;
+    output.length = 0;
+
+    input.value = ptr;
+    input.length = len;
+
+    conf = 0;
+    major = gss_wrap(&minor, port->gss->ctx, 1, GSS_C_QOP_DEFAULT,
+                     &input, &conf, &output);
+    if (GSS_ERROR(major))
+    {
+        pg_GSS_error(ERROR,
+                     gettext_noop("GSSAPI wrap error"),
+                     major, minor);
+        ret = -1;
+        goto cleanup;
+    }
+    else if (conf == 0)
+    {
+        ereport(FATAL, (errmsg("GSSAPI did not provide confidentiality")));
+        ret = -1;
+        goto cleanup;
+    }
+
+    /* format for on-wire: 4 network-order bytes of length, then payload */
+    netlen = htonl(output.length);
+    memcpy(lenbuf, &netlen, 4);
+
+    ret = secure_raw_write(port, lenbuf, 4);
+    if (ret < 0)
+    {
+        ereport(FATAL, (errmsg("Failed to send entire GSSAPI blob")));
+        return ret;
+    }
+    else if (ret < 4)
+    {
+        appendBinaryStringInfo(&port->gss->writebuf, lenbuf + ret, 4 - ret);
+        ret = 0; /* just buffer everything else */
+    }
+
+    if (ret != 0)
+        ret = secure_raw_write(port, output.value, output.length);
+
+    if (ret < 0)
+    {
+        ereport(FATAL, (errmsg("Failed to send entire GSSAPI blob")));
+        return ret;
+    }
+    else if (ret == output.length)
+    {
+        /*
+         * Strictly speaking, this isn't true; we did write more than `len`
+         * bytes.  However, this information is actually used to keep track of
+         * what has/hasn't been written yet, not actually report the number of
+         * bytes we wrote.
+         */
+        ret = len;
+        goto cleanup;
+    }
+
+    appendBinaryStringInfo(&port->gss->writebuf, (char *)output.value + ret,
+                           output.length - ret);
+
+    /* set return so that we get retried when the socket becomes writable */
+    ret = -1;
+    errno = EWOULDBLOCK;
+ cleanup:
+    if (output.value != NULL)
+        gss_release_buffer(&minor, &output);
+
+    return ret;
+}
+
+/*
+ * Wrapper function for buffering decrypted data.
+ *
+ * This allows us to present a stream-like interface to secure_read.  For a
+ * description of buffering behavior, see comment at be_gssapi_read.
+ */
+static ssize_t
+be_gssapi_read_from_buffer(Port *port, void *ptr, size_t len)
+{
+    ssize_t ret = 0;
+
+    /* Is any data available? */
+    if (port->gss->buf.len > 4 && port->gss->buf.cursor < port->gss->buf.len)
+    {
+        /* clamp length */
+        if (len > port->gss->buf.len - port->gss->buf.cursor)
+            len = port->gss->buf.len - port->gss->buf.cursor;
+
+        memcpy(ptr, port->gss->buf.data + port->gss->buf.cursor, len);
+        port->gss->buf.cursor += len;
+
+        ret = len;
+    }
+
+    /* if all data has been read, reset buffer */
+    if (port->gss->buf.cursor == port->gss->buf.len)
+    {
+        port->gss->buf.cursor = port->gss->buf.len = 0;
+        port->gss->buf.data[0] = '\0';
+    }
+
+    return ret;
+}
+
+/*
+ * Here's how the buffering works:
+ *
+ * First, we read the packet into port->gss->buf.data.  The first four bytes
+ * of this will be the network-order length of the GSSAPI-encrypted blob; from
+ * position 4 to port->gss->buf.len is then this blob.  Therefore, at this
+ * point port->gss->buf.len is the length of the blob plus 4.
+ * port->gss->buf.cursor is zero for this entire step.
+ *
+ * Then we overwrite port->gss->buf.data entirely with the decrypted contents.
+ * At this point, port->gss->buf.len reflects the actual length of the
+ * decrypted data.  port->gss->buf.cursor is then used to incrementally return
+ * this data to the caller and is therefore nonzero during this step.
+ *
+ * Once all decrypted data is returned to the caller, the cycle repeats.
+ */
+ssize_t
+be_gssapi_read(Port *port, void *ptr, size_t len)
+{
+    OM_uint32 major, minor;
+    gss_buffer_desc input, output;
+    ssize_t ret;
+    int conf = 0;
+
+    ret = be_gssapi_should_encrypt(port);
+
+    if (ret == -1)
+        return -1;
+    else if (ret == 0)
+        return secure_raw_read(port, ptr, len);
+
+    /* ensure proper behavior under recursion */
+    if (len == 0)
+        return 0;
+
+    /* report any buffered data, then recur */
+    if (port->gss->buf.cursor > 0)
+    {
+        ret = be_gssapi_read_from_buffer(port, ptr, len);
+        if (ret > 0)
+            return ret + be_gssapi_read(port, (char *)ptr + ret, len - ret);
+    }
+
+    /* our buffer is now empty */
+    if (port->gss->buf.len < 4)
+    {
+        enlargeStringInfo(&port->gss->buf, 4);
+        ret = secure_raw_read(port, port->gss->buf.data + port->gss->buf.len,
+                              4 - port->gss->buf.len);
+        if (ret < 0)
+            return ret;
+
+        /* write length to buffer */
+        port->gss->buf.len += ret;
+        port->gss->buf.data[port->gss->buf.len] = '\0';
+        if (port->gss->buf.len < 4)
+        {
+            errno = EWOULDBLOCK;
+            return -1;
+        }
+    }
+
+    /* we know the length of the packet at this point */
+    memcpy((char *)&input.length, port->gss->buf.data, 4);
+    input.length = ntohl(input.length);
+    enlargeStringInfo(&port->gss->buf, input.length - port->gss->buf.len + 4);
+
+    /* read the packet into our buffer */
+    ret = secure_raw_read(port, port->gss->buf.data + port->gss->buf.len,
+                          input.length - port->gss->buf.len + 4);
+    if (ret < 0)
+        return ret;
+
+    port->gss->buf.len += ret;
+    port->gss->buf.data[port->gss->buf.len] = '\0';
+    if (port->gss->buf.len - 4 < input.length)
+    {
+        errno = EWOULDBLOCK;
+        return -1;
+    }
+
+    /* decrypt the packet */
+    output.value = NULL;
+    output.length = 0;
+    input.value = port->gss->buf.data + 4;
+
+    major = gss_unwrap(&minor, port->gss->ctx, &input, &output, &conf, NULL);
+    if (GSS_ERROR(major))
+    {
+        pg_GSS_error(ERROR,
+                     gettext_noop("GSSAPI unwrap error"),
+                     major, minor);
+        ret = -1;
+        goto cleanup;
+    }
+    else if (conf == 0)
+    {
+        ereport(FATAL, (errmsg("GSSAPI did not provide confidentiality")));
+        ret = -1;
+        goto cleanup;
+    }
+
+    /* load decrypted packet into our buffer, then recur */
+    port->gss->buf.cursor = port->gss->buf.len = 0;
+    port->gss->buf.data[0] = '\0';
+    enlargeStringInfo(&port->gss->buf, output.length);
+
+    memcpy(port->gss->buf.data, output.value, output.length);
+    port->gss->buf.len = output.length;
+    port->gss->buf.data[port->gss->buf.len] = '\0';
+
+    ret = be_gssapi_read_from_buffer(port, ptr, len);
+ cleanup:
+    if (output.value != NULL)
+        gss_release_buffer(&minor, &output);
+
+    return ret;
+}
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index ac709d1..d43fa1b 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -132,6 +132,14 @@ retry:
     }
     else
 #endif
+#ifdef ENABLE_GSS
+    if (port->gss != NULL)
+    {
+        n = be_gssapi_read(port, ptr, len);
+        waitfor = WL_SOCKET_READABLE;
+    }
+    else
+#endif
     {
         n = secure_raw_read(port, ptr, len);
         waitfor = WL_SOCKET_READABLE;
@@ -234,6 +242,14 @@ retry:
     }
     else
 #endif
+#ifdef ENABLE_GSS
+    if (port->gss != NULL)
+    {
+        n = be_gssapi_write(port, ptr, len);
+        waitfor = WL_SOCKET_WRITEABLE;
+    }
+    else
+#endif
     {
         n = secure_raw_write(port, ptr, len);
         waitfor = WL_SOCKET_WRITEABLE;
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 28f9fb5..509b9ab 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/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index b16fc28..62cfd4c 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -2036,6 +2036,10 @@ retry1:
                 port->user_name = pstrdup(valptr);
             else if (strcmp(nameptr, "options") == 0)
                 port->cmdline_options = pstrdup(valptr);
+#ifdef ENABLE_GSS
+            else if (strcmp(nameptr, "gss_encrypt") == 0)
+                port->gss->gss_encrypt = pstrdup(valptr);
+#endif
             else if (strcmp(nameptr, "replication") == 0)
             {
                 /*
@@ -2355,6 +2359,10 @@ ConnCreate(int serverFd)
         ExitPostmaster(1);
     }
 #endif
+#ifdef ENABLE_GSS
+    initStringInfo(&port->gss->buf);
+    initStringInfo(&port->gss->writebuf);
+#endif
 #endif

     return port;
@@ -2371,7 +2379,15 @@ ConnFree(Port *conn)
     secure_close(conn);
 #endif
     if (conn->gss)
+    {
+#ifdef ENABLE_GSS
+        if (conn->gss->buf.data)
+            pfree(conn->gss->buf.data);
+        if (conn->gss->writebuf.data)
+            pfree(conn->gss->writebuf.data);
+#endif
         free(conn->gss);
+    }
     free(conn);
 }

diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index e22d4db..de1552a 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,19 @@ process_startup_options(Port *port, bool am_superuser)

         SetConfigOption(name, value, gucctx, PGC_S_CLIENT);
     }
+
+#ifdef ENABLE_GSS
+    /* delay processing until after AUTH_REQ_OK has been sent */
+    if (port->gss->gss_encrypt != NULL)
+        port->gss->encrypt = !strcmp(port->gss->gss_encrypt, "on");
+
+    if (!port->gss->encrypt && port->hba->require_encrypt)
+    {
+        ereport(FATAL, (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
+                        errmsg("User \"%s\" is required to use GSSAPI encryption but did not request it",
+                               port->user_name)));
+    }
+#endif
 }

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

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

-
 /*
  * Displayable names for context types (enum GucContext)
  *
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 5d07b78..71c1f95 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -68,6 +68,7 @@ typedef struct
 #include "datatype/timestamp.h"
 #include "libpq/hba.h"
 #include "libpq/pqcomm.h"
+#include "lib/stringinfo.h"


 typedef enum CAC_state
@@ -88,6 +89,10 @@ typedef struct
     gss_cred_id_t cred;            /* GSSAPI connection cred's */
     gss_ctx_id_t ctx;            /* GSSAPI connection context */
     gss_name_t    name;            /* GSSAPI client name */
+    StringInfoData buf;            /* GSSAPI encryption data buffering */
+    StringInfoData writebuf;    /* GSSAPI nonblocking write buffering */
+    char *gss_encrypt;            /* GSSAPI encryption literal request */
+    bool encrypt;                /* GSSAPI encryption has started */
 #endif
 } pg_gssinfo;
 #endif
@@ -214,6 +219,11 @@ 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
+ssize_t be_gssapi_read(Port *port, void *ptr, size_t len);
+ssize_t be_gssapi_write(Port *port, void *ptr, size_t len);
+#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 0569994..0c7653c 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 0ea87b6..e834d12 100644
--- a/src/interfaces/libpq/Makefile
+++ b/src/interfaces/libpq/Makefile
@@ -49,7 +49,7 @@ OBJS += fe-secure-openssl.o
 endif

 ifeq ($(with_gssapi),yes)
-OBJS += fe-gssapi-common.o
+OBJS += fe-gssapi-common.o fe-secure-gssapi.o
 endif

 ifeq ($(PORTNAME), cygwin)
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 5ad4755..8c154ed 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -72,6 +72,10 @@ static int ldapServiceLookup(const char *purl, PQconninfoOption *options,
                   PQExpBuffer errorMessage);
 #endif

+#ifdef ENABLE_GSS
+#include "fe-gssapi-common.h"
+#endif
+
 #include "libpq/ip.h"
 #include "mb/pg_wchar.h"

@@ -91,8 +95,10 @@ 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 GSSAPI 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 +302,12 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
     offsetof(struct pg_conn, gsslib)},
 #endif

+#if defined(ENABLE_GSS)
+    {"gss_require_encrypt", "GSS_REQUIRE_ENCRYPT", "0", NULL,
+        "Require-GSS-encryption", "", 1, /* should be '0' or '1' */
+     offsetof(struct pg_conn, gss_require_encrypt)},
+#endif
+
     {"replication", NULL, NULL, NULL,
         "Replication", "D", 5,
     offsetof(struct pg_conn, replication)},
@@ -2518,6 +2530,38 @@ 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;
+
+                    if (pg_GSS_should_encrypt(conn) && conn->inEnd > conn->inStart)
+                    {
+                        /*
+                         * If we've any data from the server buffered, it's
+                         * encrypted and we need to decrypt it.  Pass it back
+                         * down a layer to decrypt.  At this point in time,
+                         * conn->inStart and conn->inCursor match.
+                         */
+                        int n;
+
+                        appendBinaryPQExpBuffer(&conn->gbuf,
+                                                conn->inBuffer + conn->inStart,
+                                                conn->inEnd - conn->inStart);
+                        conn->inEnd = conn->inStart;
+
+                        /* Will not block on nonblocking sockets */
+                        n = pg_GSS_read(conn, conn->inBuffer + conn->inEnd,
+                                        conn->inBufSize - conn->inEnd);
+                        /*
+                         * If n < 0, then this wasn't a full request and
+                         * either more data will be available later to
+                         * complete it or we will error out then.
+                         */
+                        if (n > 0)
+                            conn->inEnd += n;
+                    }
+#endif
+
                     /*
                      * Set asyncStatus so that PQgetResult will think that
                      * what comes back next is the result of a query.  See
@@ -2558,6 +2602,47 @@ 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_require_encrypt != '1')
+                    {
+                        /*
+                         * We tried to request GSSAPI 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.
+                         */
+                        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;
+                            conn->gss_auth_done = false;
+                            conn->gss_decrypted = false;
+
+                            /* Must drop the old connection */
+                            pqDropConnection(conn, true);
+                            conn->status = CONNECTION_NEEDED;
+                            gss_delete_sec_context(&minor, &conn->gctx,
+                                                   GSS_C_NO_BUFFER);
+
+                            resetPQExpBuffer(&conn->gbuf);
+                            resetPQExpBuffer(&conn->gwritebuf);
+                            goto keep_going;
+                        }
+                    }
+                    else if (*conn->gss_require_encrypt == '1')
+                    {
+                        appendPQExpBufferStr(&conn->errorMessage,
+                                             libpq_gettext("Server does not support required GSSAPI encryption\n"));
+                    }
+#endif
                     else if (conn->send_appname &&
                              (conn->appname || conn->fbappname))
                     {
@@ -2575,7 +2660,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;
@@ -2798,6 +2883,10 @@ makeEmptyPGconn(void)
     conn->wait_ssl_try = false;
     conn->ssl_in_use = false;
 #endif
+#ifdef ENABLE_GSS
+    initPQExpBuffer(&conn->gbuf);
+    initPQExpBuffer(&conn->gwritebuf);
+#endif

     /*
      * We try to send at least 8K at a time, which is the usual size of pipe
@@ -2914,6 +3003,10 @@ freePGconn(PGconn *conn)
     if (conn->krbsrvname)
         free(conn->krbsrvname);
 #endif
+#if defined(ENABLE_GSS)
+    termPQExpBuffer(&conn->gbuf);
+    termPQExpBuffer(&conn->gwritebuf);
+#endif
 #if defined(ENABLE_GSS) && defined(ENABLE_SSPI)
     if (conn->gsslib)
         free(conn->gsslib);
@@ -3019,6 +3112,8 @@ closePGconn(PGconn *conn)
             gss_release_buffer(&min_s, &conn->ginbuf);
         if (conn->goutbuf.length)
             gss_release_buffer(&min_s, &conn->goutbuf);
+        resetPQExpBuffer(&conn->gbuf);
+        resetPQExpBuffer(&conn->gwritebuf);
     }
 #endif
 #ifdef ENABLE_SSPI
diff --git a/src/interfaces/libpq/fe-gssapi-common.c b/src/interfaces/libpq/fe-gssapi-common.c
index bc2c977..22b9104 100644
--- a/src/interfaces/libpq/fe-gssapi-common.c
+++ b/src/interfaces/libpq/fe-gssapi-common.c
@@ -61,3 +61,15 @@ pg_GSS_error(const char *mprefix, PGconn *conn,
     /* Add the minor codes as well */
     pg_GSS_error_int(&conn->errorMessage, mprefix, min_stat, GSS_C_MECH_CODE);
 }
+
+/*
+ * Only consider encryption when GSS context is complete
+ */
+bool
+pg_GSS_should_encrypt(PGconn *conn)
+{
+    if (conn->gctx == GSS_C_NO_CONTEXT)
+        return false;
+    else
+        return conn->gss_auth_done && !conn->gss_disable_enc;
+}
diff --git a/src/interfaces/libpq/fe-gssapi-common.h b/src/interfaces/libpq/fe-gssapi-common.h
index 4b31371..e23f524 100644
--- a/src/interfaces/libpq/fe-gssapi-common.h
+++ b/src/interfaces/libpq/fe-gssapi-common.h
@@ -17,5 +17,6 @@

 void pg_GSS_error(const char *mprefix, PGconn *conn,
                   OM_uint32 maj_stat, OM_uint32 min_stat);
+bool pg_GSS_should_encrypt(PGconn *conn);

 #endif /* FE_GSSAPI_COMMON_H */
diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c
index 43898a4..296d2fd 100644
--- a/src/interfaces/libpq/fe-protocol3.c
+++ b/src/interfaces/libpq/fe-protocol3.c
@@ -2125,6 +2125,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/fe-secure-gssapi.c b/src/interfaces/libpq/fe-secure-gssapi.c
new file mode 100644
index 0000000..322b3cc
--- /dev/null
+++ b/src/interfaces/libpq/fe-secure-gssapi.c
@@ -0,0 +1,330 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-secure-gssapi.c
+ *   The front-end (client) encryption support for GSSAPI
+ *
+ * Portions Copyright (c) 2016, 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"
+
+/*
+ * Send a message along the connection, possibly using GSSAPI.
+ *
+ * If not encrypting at the call-time, we send plaintext following calling
+ * conventions of pqsecure_raw_write.  Partial writes are supported using a
+ * dedicated PQExpBuffer in conn.  A partial write will return 0; otherwise,
+ * we return -1 (for error) or the number of total bytes written in the write
+ * of the current ptr.  Calling with a new value of ptr after a partial write
+ * is undefined.
+ */
+ssize_t
+pg_GSS_write(PGconn *conn, void *ptr, size_t len)
+{
+    OM_uint32 major, minor;
+    gss_buffer_desc input, output;
+    ssize_t ret;
+    int conf;
+    uint32 netlen;
+    char lenbuf[4];
+
+    ret = pg_GSS_should_encrypt(conn);
+
+    if (ret == -1)
+        return -1;
+    else if (ret == 0)
+        return pqsecure_raw_write(conn, ptr, len);
+
+    /* send any data we have buffered */
+    if (conn->gwritebuf.len != 0)
+    {
+        ret = pqsecure_raw_write(conn,
+                                 conn->gwritebuf.data + conn->gwritecurs,
+                                 conn->gwritebuf.len - conn->gwritecurs);
+        if (ret < 0)
+            return ret;
+
+        conn->gwritecurs += ret;
+
+        /* update and possibly clear buffer state */
+        if (conn->gwritecurs == conn->gwritebuf.len)
+        {
+            conn->gwritebuf.len = conn->gwritecurs = 0;
+            conn->gwritebuf.data[0] = '\0';
+
+            /* The entire request has now been written */
+            return len;
+        }
+
+        /* need to be called again */
+        return 0;
+    }
+
+    /* encrypt the message */
+    output.value = NULL;
+    output.length = 0;
+
+    input.value = ptr;
+    input.length = len;
+
+    conf = 0;
+    major = gss_wrap(&minor, conn->gctx, 1, GSS_C_QOP_DEFAULT,
+                     &input, &conf, &output);
+    if (GSS_ERROR(major))
+    {
+        pg_GSS_error(libpq_gettext("GSSAPI wrap error"), conn,
+                     major, minor);
+        ret = -1;
+        goto cleanup;
+    }
+    else if (conf == 0)
+    {
+        printfPQExpBuffer(&conn->errorMessage, libpq_gettext(
+                              "GSSAPI did not provide confidentiality\n"));
+        ret = -1;
+        goto cleanup;
+    }
+
+    /* format for on-wire: 4 network-order bytes of length, then payload */
+    netlen = htonl(output.length);
+    memcpy(lenbuf, &netlen, 4);
+
+    ret = pqsecure_raw_write(conn, lenbuf, 4);
+    if (ret < 0)
+    {
+        printfPQExpBuffer(&conn->errorMessage, libpq_gettext(
+                              "GSSAPI write failed to send everything\n"));
+        ret = -1;
+        goto cleanup;
+    }
+
+    /* buffer any leftovers from a partial write */
+    if (ret < 4)
+    {
+        appendBinaryPQExpBuffer(&conn->gwritebuf, lenbuf + ret, 4 - ret);
+        ret = 0; /* just buffer everything else */
+    }
+
+    if (ret != 0)
+        ret = pqsecure_raw_write(conn, output.value, output.length);
+
+    if (ret == output.length)
+    {
+        ret = len; /* all requested bytes now written */
+        goto cleanup;
+    }
+
+    appendBinaryPQExpBuffer(&conn->gwritebuf, (char *)output.value + 4 - ret,
+                            output.length + 4 - ret);
+
+    /* set return so that we get retried when the socket becomes writable */
+    ret = 0;
+ cleanup:
+    if (output.value != NULL)
+        gss_release_buffer(&minor, &output);
+
+    return ret;
+}
+
+/*
+ * Wrapper function for buffering decrypted data.
+ *
+ * This allows us to present a stream-like interface to pqsecure_read.  For a
+ * description of buffering, see comment at be_gssapi_read (in be-gssapi.c).
+ */
+static ssize_t
+pg_GSS_read_from_buffer(PGconn *conn, void *ptr, size_t len)
+{
+    ssize_t ret = 0;
+
+    /* Is any data available? */
+    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;
+    }
+
+    /* if all data has been read, reset buffer */
+    if (conn->gcursor == conn->gbuf.len)
+    {
+        conn->gcursor = conn->gbuf.len = 0;
+        conn->gbuf.data[0] = '\0';
+    }
+
+    return ret;
+}
+
+/*
+ * Buffering behaves as in be_gssapi_read (in be-gssapi.c).  Because this is
+ * the frontend, we use a PQExpBuffer at conn->gbuf instead of a StringInfo,
+ * and so there is an additional, separate cursor field in the structure.
+ */
+ssize_t
+pg_GSS_read(PGconn *conn, void *ptr, size_t len)
+{
+    OM_uint32 major, minor;
+    gss_buffer_desc input, output;
+    ssize_t ret;
+    int conf = 0;
+
+    ret = pg_GSS_should_encrypt(conn);
+
+    if (ret == -1)
+        return -1;
+    else if (ret == 0)
+        return pqsecure_raw_read(conn, ptr, len);
+
+    /* ensure proper behavior under recursion */
+    if (len == 0)
+        return 0;
+
+    /* report any buffered data, then recur */
+    if (conn->gcursor > 0)
+    {
+        ret = pg_GSS_read_from_buffer(conn, ptr, len);
+
+        /* Pass up error message fragments.  See comment below. */
+        if (!conn->gss_decrypted && conn->gcursor == conn->gbuf.len)
+        {
+            /* call _raw_read to get any remaining parts of the message */
+            gss_delete_sec_context(&minor, &conn->gctx, GSS_C_NO_BUFFER);
+            conn->gctx = 0;
+        }
+
+        if (ret > 0)
+            return ret + pg_GSS_read(conn, (char *)ptr + ret, len - ret);
+    }
+
+    /* our buffer is now empty */
+    if (conn->gbuf.len < 4)
+    {
+        ret = enlargePQExpBuffer(&conn->gbuf, 4);
+        if (ret != 1)
+        {
+            printfPQExpBuffer(&conn->errorMessage, libpq_gettext(
+                                  "Failed to fit packet length in buffer\n"));
+            return -1;
+        }
+
+        ret = pqsecure_raw_read(conn, conn->gbuf.data, 4);
+        if (ret < 0)
+            /* error already set by secure_raw_read */
+            return ret;
+
+        /* write length to buffer */
+        conn->gbuf.len += ret;
+        conn->gbuf.data[conn->gbuf.len] = '\0';
+        if (conn->gbuf.len < 4)
+            return 0;
+    }
+
+    /*
+     * We can receive error messages from old servers that don't support
+     * GSSAPI encryption at this time.  They need to be passed up so that we
+     * can potentially reconnect.
+     *
+     * This limits the server's first reply to not be between 1157627904
+     * (about 2**30) and 1174405119, which are both over a gigabyte in size.
+     * If the server sends a connection parameter status message of this size,
+     * there are other problems present.
+     */
+    if (!conn->gss_decrypted && conn->gbuf.data[0] == 'E')
+    {
+        ret = pg_GSS_read_from_buffer(conn, ptr, len);
+        if (conn->gcursor == conn->gbuf.len)
+        {
+            /* Call _raw_read to get any remaining parts of the message */
+            gss_delete_sec_context(&minor, &conn->gctx, GSS_C_NO_BUFFER);
+            conn->gctx = 0;
+        }
+        return ret;
+    }
+
+    /* we know the length of the packet at this point */
+    memcpy((char *)&input.length, conn->gbuf.data, 4);
+    input.length = ntohl(input.length);
+    ret = enlargePQExpBuffer(&conn->gbuf, input.length - conn->gbuf.len + 4);
+    if (ret != 1)
+    {
+        printfPQExpBuffer(&conn->errorMessage, libpq_gettext(
+                              "GSSAPI encrypted packet (length %ld) too big\n"),
+                          input.length);
+        return -1;
+    }
+
+    /* load any remaining parts of the packet into our buffer */
+    if (conn->gbuf.len - 4 < input.length)
+    {
+        ret = pqsecure_raw_read(conn, conn->gbuf.data + conn->gbuf.len,
+                                input.length - 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 < input.length)
+            return 0;
+    }
+
+    /* decrypt the packet */
+    output.value = NULL;
+    output.length = 0;
+    input.value = conn->gbuf.data + 4;
+
+    major = gss_unwrap(&minor, conn->gctx, &input, &output, &conf, NULL);
+    if (GSS_ERROR(major))
+    {
+        pg_GSS_error(libpq_gettext("GSSAPI unwrap error"), conn,
+                     major, minor);
+        ret = -1;
+        goto cleanup;
+    }
+    else if (conf == 0)
+    {
+        printfPQExpBuffer(&conn->errorMessage, libpq_gettext(
+                              "GSSAPI did not provide confidentiality\n"));
+        ret = -1;
+        goto cleanup;
+    }
+
+    conn->gss_decrypted = true;
+
+    /* load decrypted packet into our buffer, then recur */
+    conn->gcursor = conn->gbuf.len = 0;
+    conn->gbuf.data[0] = '\0';
+    ret = enlargePQExpBuffer(&conn->gbuf, output.length);
+    if (ret != 1)
+    {
+        printfPQExpBuffer(&conn->errorMessage, libpq_gettext(
+                              "GSSAPI decrypted packet (length %ld) too big\n"),
+                          output.length);
+        return -1;
+    }
+
+    memcpy(conn->gbuf.data, output.value, output.length);
+    conn->gbuf.len = output.length;
+    conn->gbuf.data[conn->gbuf.len] = '\0';
+
+    ret = pg_GSS_read_from_buffer(conn, ptr, len);
+
+ cleanup:
+    if (output.value != NULL)
+        gss_release_buffer(&minor, &output);
+    return ret;
+}
diff --git a/src/interfaces/libpq/fe-secure.c b/src/interfaces/libpq/fe-secure.c
index 94e47a5..14fba1f 100644
--- a/src/interfaces/libpq/fe-secure.c
+++ b/src/interfaces/libpq/fe-secure.c
@@ -213,6 +213,13 @@ pqsecure_read(PGconn *conn, void *ptr, size_t len)
     }
     else
 #endif
+#ifdef ENABLE_GSS
+    if (conn->gctx != GSS_C_NO_CONTEXT)
+    {
+        n = pg_GSS_read(conn, ptr, len);
+    }
+    else
+#endif
     {
         n = pqsecure_raw_read(conn, ptr, len);
     }
@@ -279,7 +286,7 @@ pqsecure_raw_read(PGconn *conn, void *ptr, size_t len)
  * to determine whether to continue/retry after error.
  */
 ssize_t
-pqsecure_write(PGconn *conn, const void *ptr, size_t len)
+pqsecure_write(PGconn *conn, void *ptr, size_t len)
 {
     ssize_t        n;

@@ -290,6 +297,13 @@ pqsecure_write(PGconn *conn, const void *ptr, size_t len)
     }
     else
 #endif
+#ifdef ENABLE_GSS
+    if (conn->gctx != GSS_C_NO_CONTEXT)
+    {
+        n = pg_GSS_write(conn, ptr, len);
+    }
+    else
+#endif
     {
         n = pqsecure_raw_write(conn, ptr, len);
     }
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 6c9bbf7..b00b03b 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -23,6 +23,7 @@
 /* We assume libpq-fe.h has already been included. */
 #include "postgres_fe.h"
 #include "libpq-events.h"
+#include "lib/stringinfo.h"

 #include <time.h>
 #include <sys/types.h>
@@ -445,6 +446,14 @@ struct pg_conn
     gss_name_t    gtarg_nam;        /* GSS target name */
     gss_buffer_desc ginbuf;        /* GSS input token */
     gss_buffer_desc goutbuf;    /* GSS output token */
+    PQExpBufferData gbuf;        /* GSS encryption buffering */
+    size_t gcursor;                /* GSS buffering position */
+    PQExpBufferData gwritebuf;    /* GSS nonblocking write buffering */
+    size_t gwritecurs;            /* GSS write buffer position */
+    bool gss_disable_enc;        /* GSS encryption recognized by server */
+    bool gss_auth_done;            /* GSS authentication finished */
+    bool gss_decrypted;            /* GSS decrypted first message */
+    char *gss_require_encrypt;    /* GSS encryption required */
 #endif

 #ifdef ENABLE_SSPI
@@ -620,7 +629,7 @@ extern void pqsecure_destroy(void);
 extern PostgresPollingStatusType pqsecure_open_client(PGconn *);
 extern void pqsecure_close(PGconn *);
 extern ssize_t pqsecure_read(PGconn *, void *ptr, size_t len);
-extern ssize_t pqsecure_write(PGconn *, const void *ptr, size_t len);
+extern ssize_t pqsecure_write(PGconn *, void *ptr, size_t len);
 extern ssize_t pqsecure_raw_read(PGconn *, void *ptr, size_t len);
 extern ssize_t pqsecure_raw_write(PGconn *, const void *ptr, size_t len);

@@ -642,6 +651,14 @@ extern bool pgtls_read_pending(PGconn *conn);
 extern ssize_t pgtls_write(PGconn *conn, const void *ptr, size_t len);

 /*
+ * The GSSAPI backend in fe-secure-gssapi.c provides these functions.
+ */
+#ifdef ENABLE_GSS
+extern ssize_t pg_GSS_read(PGconn *conn, void *ptr, size_t len);
+extern ssize_t pg_GSS_write(PGconn *conn, void *ptr, size_t len);
+#endif
+
+/*
  * this is so that we can check if a connection is non-blocking internally
  * without the overhead of a function call
  */
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 2fa2509..5e30739 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -156,8 +156,7 @@ sub mkvcbuild
     $postgres->FullExportDLL('postgres.lib');

     # The OBJS scraper doesn't know about ifdefs, so remove be-secure-openssl.c
-    # if building without OpenSSL, and be-gssapi-common.c when building with
-    # GSSAPI.
+    # if building without OpenSSL, and GSSAPI files when building with it.
     if (!$solution->{options}->{openssl})
     {
         $postgres->RemoveFile('src/backend/libpq/be-secure-openssl.c');
@@ -165,6 +164,7 @@ sub mkvcbuild
     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', '',
@@ -216,8 +216,7 @@ sub mkvcbuild
     $libpq->AddReference($libpgport);

     # The OBJS scraper doesn't know about ifdefs, so remove fe-secure-openssl.c
-    # if building without OpenSSL, and fe-gssapi-common.c when building with
-    # GSSAPI
+    # if building without OpenSSL, and GSSAPI-only files when building with it.
     if (!$solution->{options}->{openssl})
     {
         $libpq->RemoveFile('src/interfaces/libpq/fe-secure-openssl.c');
@@ -225,6 +224,7 @@ sub mkvcbuild
     if (!$solution->{options}->{gss})
     {
         $libpq->RemoveFile('src/interfaces/libpq/fe-gssapi-common.c');
+        $libpq->RemoveFile('src/interfaces/libpq/fe-secure-gssapi.c');
     }

     my $libpqwalreceiver =
--
2.8.0.rc3

From 6cd8e3756766ae6ae568659bdb6cae8332ad3f2c Mon Sep 17 00:00:00 2001
From: Robbie Harwood <rharwood@redhat.com>
Date: Tue, 8 Mar 2016 17:16:29 -0500
Subject: [PATCH 3/3] GSSAPI authentication cleanup

Become more fussy about what flags we need.  Now that we want to do
encryption, protection and integrity are needed for that to work.  Other
protections are desirable as well, and we should check the flags GSSAPI
returns to us.

Also remove the (now-)redundant definitions that worked around an old
bug with MIT Kerberos on Windows.

Finally, resolve a pre-existing action item for acquiring all possible GSSAPI
error messages on the backend, rather than just the first major and first
minor statuses.
---
 src/backend/libpq/auth.c                | 20 ++++++++++++-
 src/backend/libpq/be-gssapi-common.c    | 50 ++++++++++++++++-----------------
 src/interfaces/libpq/fe-auth.c          | 19 +++++++++++--
 src/interfaces/libpq/fe-gssapi-common.c | 11 --------
 4 files changed, 59 insertions(+), 41 deletions(-)

diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 94d95bd..661228e 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -719,7 +719,8 @@ pg_GSS_recvauth(Port *port)
     OM_uint32    maj_stat,
                 min_stat,
                 lmin_s,
-                gflags;
+                gflags,
+                target_flags;
     int            mtype;
     int            ret;
     StringInfoData buf;
@@ -875,6 +876,23 @@ pg_GSS_recvauth(Port *port)
     }

     /*
+     * GSS_C_REPLAY_FLAG (request replay detection) and GSS_C_SEQUENCE_FLAG
+     * (out-of-sequence detection) are missing for compatibility with older
+     * clients which do not request these flags.  They should be added in as
+     * soon as we no longer care about pre-9.6 clients.
+     *
+     * Newer clients will request both GSS_C_REPLAY_FLAGS and
+     * GSS_C_SEQUENCE_FLAGS as well as the flags we check for below, so lack
+     * of these two flags is not the end of the world.
+     */
+    target_flags = GSS_C_MUTUAL_FLAG | GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG;
+    if ((gflags & target_flags) != target_flags)
+    {
+        ereport(FATAL, (errmsg("GSSAPI did not provide required flags")));
+        return STATUS_ERROR;
+    }
+
+    /*
      * GSS_S_COMPLETE indicates that authentication is now complete.
      *
      * Get the name of the user that authenticated, and compare it to the pg
diff --git a/src/backend/libpq/be-gssapi-common.c b/src/backend/libpq/be-gssapi-common.c
index dc27fa8..6cf5513 100644
--- a/src/backend/libpq/be-gssapi-common.c
+++ b/src/backend/libpq/be-gssapi-common.c
@@ -17,17 +17,6 @@

 #include "libpq/be-gssapi-common.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)
 {
@@ -36,31 +25,40 @@ pg_GSS_error(int severity, char *errmsg, OM_uint32 maj_stat, OM_uint32 min_stat)
                 msg_ctx;
     char        msg_major[128],
                 msg_minor[128];
+    short        i;
+
+    gmsg.value = NULL;
+    gmsg.length = 0;

     /* 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);
+    i = 0;
+    do
+    {
+        gss_display_status(&lmin_s, maj_stat, GSS_C_GSS_CODE,
+                           GSS_C_NO_OID, &msg_ctx, &gmsg);
+        strlcpy(msg_major + i, gmsg.value, sizeof(msg_major) - i);
+        gss_release_buffer(&lmin_s, &gmsg);
+    }
+    while (msg_ctx && i < sizeof(msg_major));

-    if (msg_ctx)
-
-        /*
-         * More than one message available. XXX: Should we loop and read all
-         * messages? (same below)
-         */
+    if (msg_ctx || i == sizeof(msg_major))
         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);
+    i = 0;
+    do
+    {
+        gss_display_status(&lmin_s, min_stat, GSS_C_MECH_CODE,
+                           GSS_C_NO_OID, &msg_ctx, &gmsg);
+        strlcpy(msg_minor + i, gmsg.value, sizeof(msg_minor) - i);
+        gss_release_buffer(&lmin_s, &gmsg);
+    }
+    while (msg_ctx && i < sizeof(msg_minor));

-    if (msg_ctx)
+    if (msg_ctx || i == sizeof(msg_minor))
         ereport(WARNING,
                 (errmsg_internal("incomplete GSS minor error report")));

diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c
index 56528f2..76bc3ec 100644
--- a/src/interfaces/libpq/fe-auth.c
+++ b/src/interfaces/libpq/fe-auth.c
@@ -57,20 +57,24 @@ pg_GSS_continue(PGconn *conn)
 {
     OM_uint32    maj_stat,
                 min_stat,
-                lmin_s;
+                lmin_s,
+                req_flags,
+                ret_flags;

+    req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG |
+        GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG;
     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,
+                                    req_flags,
                                     0,
                                     GSS_C_NO_CHANNEL_BINDINGS,
           (conn->gctx == GSS_C_NO_CONTEXT) ? GSS_C_NO_BUFFER : &conn->ginbuf,
                                     NULL,
                                     &conn->goutbuf,
-                                    NULL,
+                                    &ret_flags,
                                     NULL);

     if (conn->gctx != GSS_C_NO_CONTEXT)
@@ -109,8 +113,17 @@ pg_GSS_continue(PGconn *conn)
     }

     if (maj_stat == GSS_S_COMPLETE)
+    {
         gss_release_name(&lmin_s, &conn->gtarg_nam);

+        if ((ret_flags & req_flags) != req_flags)
+        {
+            printfPQExpBuffer(&conn->errorMessage,
+                              libpq_gettext("GSSAPI did not provide required flags\n"));
+            return STATUS_ERROR;
+        }
+    }
+
     return STATUS_OK;
 }

diff --git a/src/interfaces/libpq/fe-gssapi-common.c b/src/interfaces/libpq/fe-gssapi-common.c
index 22b9104..446a669 100644
--- a/src/interfaces/libpq/fe-gssapi-common.c
+++ b/src/interfaces/libpq/fe-gssapi-common.c
@@ -15,17 +15,6 @@
 #include "fe-auth.h"
 #include "fe-gssapi-common.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".
  */
--
2.8.0.rc3


Attachment

pgsql-hackers by date:

Previous
From: Petr Jelinek
Date:
Subject: Re: Re: [COMMITTERS] pgsql: Enable logical slots to follow timeline switches
Next
From: Amit Kapila
Date:
Subject: Re: Parallel Queries and PostGIS