Re: [PATCH v5] GSSAPI encryption support

Поиск
Список
Период
Сортировка
От Robbie Harwood
Тема Re: [PATCH v5] GSSAPI encryption support
Дата
Msg-id jlgtwl9g95m.fsf@thriss.redhat.com
обсуждение исходный текст
Ответ на Re: [PATCH v4] GSSAPI encryption support  (David Steele <david@pgmasters.net>)
Ответы Re: [PATCH v5] GSSAPI encryption support  (David Steele <david@pgmasters.net>)
Re: [PATCH v5] GSSAPI encryption support  (Michael Paquier <michael.paquier@gmail.com>)
Список pgsql-hackers
David Steele <david@pgmasters.net> writes:

> Hi Robbie,
>
> On 2/10/16 4:06 PM, Robbie Harwood wrote:
>> Hello friends,
>>
>> For your consideration, here is a new version of GSSAPI encryption
>> support.  For those who prefer, it's also available on my github:
>> https://github.com/frozencemetery/postgres/commit/c92275b6605d7929cda5551de47a4c60aab7179e
>
> It tried out this patch and ran into a few problems:
>
> 1) It didn't apply cleanly to HEAD.  It did apply cleanly on a455878
> which I figured was recent enough for testing.  I didn't bisect to find
> the exact commit that broke it.

It applied to head of master (57c932475504d63d8f8a68fc6925d7decabc378a)
for me (`patch -p1 < v4-GSSAPI-encryption-support.patch`).  I rebased it
anyway and cut a v5 anyway, just to be sure.  It's attached, and
available on github as well:
https://github.com/frozencemetery/postgres/commit/dc10e3519f0f6c67f79abd157dc8ff1a1c293f53

> 2) While I was able to apply the patch and get it compiled it seemed
> pretty flaky - I was only able to logon about 1 in 10 times on average.
>  Here was my testing methodology:
>
> a) Build Postgres from a455878 (without your patch), install/configure
> Kerberos and get everything working.  I was able the set the auth method
> to gss in pg_hba.conf and logon successfully every time.
>
> b) On the same system rebuild Postgres from a455878 including your patch
> and attempt authentication.
>
> The problems arose after step 2b.  Sometimes I would try to logon twenty
> times without success and sometimes it only take five or six attempts.
> I was never able to logon successfully twice in a row.
>
> When not successful the client always output this incomplete message
> (without  terminating LF):
>
> psql: expected authentication request from server, but received
>
> From the logs I can see the server is reporting EOF from the client,
> though the client does not core dump and prints the above message before
> exiting.
>
> I have attached files that contain server logs at DEBUG5 and tcpdump
> output for both the success and failure cases.
>
> Please let me know if there's any more information you would like me to
> provide.

What I can't tell from looking at your methodology is whether both the
client and server were running my patches or no.  There's no fallback
here (I'd like to talk about how that should work, with example from
v1-v3, if people have ideas).  This means that both the client and the
server need to be running my patches for the moment.  Is this your
setup?

Thanks for taking it for a spin!
--Robbie

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

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

diff --git a/configure b/configure
index 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/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml
index 3b2935c..7d37223 100644
--- a/doc/src/sgml/client-auth.sgml
+++ b/doc/src/sgml/client-auth.sgml
@@ -915,7 +915,7 @@ omicron         bryanh                  guest1
     provides automatic authentication (single sign-on) for systems
     that support it. The authentication itself is secure, but the
     data sent over the database connection will be sent unencrypted unless
-    <acronym>SSL</acronym> is used.
+    <acronym>SSL</acronym> or <acronym>GSSAPI</acronym> are used.
    </para>

    <para>
diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml
index 209eb9e..f76b79b 100644
--- a/doc/src/sgml/runtime.sgml
+++ b/doc/src/sgml/runtime.sgml
@@ -1915,12 +1915,13 @@ pg_dumpall -p 5432 | psql -d postgres -p 5433
   </para>

   <para>
-   To prevent spoofing on TCP connections, the best solution is to use
-   SSL certificates and make sure that clients check the server's certificate.
-   To do that, the server
-   must be configured to accept only <literal>hostssl</> connections (<xref
-   linkend="auth-pg-hba-conf">) and have SSL key and certificate files
-   (<xref linkend="ssl-tcp">). The TCP client must connect using
+   To prevent spoofing on TCP connections, the best solutions are either to
+   use GSSAPI for authentication and encryption or to use SSL certificates and
+   make sure that clients check the server's certificate.  To secure using
+   SSL, the server must be configured to accept only <literal>hostssl</>
+   connections (<xref linkend="auth-pg-hba-conf">) and have SSL key and
+   certificate files (<xref linkend="ssl-tcp">). The TCP client must connect
+   using
    <literal>sslmode=verify-ca</> or
    <literal>verify-full</> and have the appropriate root certificate
    file installed (<xref linkend="libq-ssl-certificates">).
@@ -2040,6 +2041,13 @@ pg_dumpall -p 5432 | psql -d postgres -p 5433
       connect to servers only via SSL. <application>Stunnel</> or
       <application>SSH</> can also be used to encrypt transmissions.
      </para>
+
+     <para>
+       Similarly, GSSAPI also encrypts all data sent across the network,
+       including passwords, queries, and data, as in
+       SSL. <filename>pg_hba.conf</> allows specification of GSSAPI
+       connections, which are always encrypted.
+     </para>
     </listitem>
   </varlistentry>

diff --git a/src/Makefile.global.in b/src/Makefile.global.in
index 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..b80ae74 100644
--- a/src/backend/libpq/Makefile
+++ b/src/backend/libpq/Makefile
@@ -21,4 +21,8 @@ ifeq ($(with_openssl),yes)
 OBJS += be-secure-openssl.o
 endif

+ifeq ($(with_gssapi),yes)
+OBJS += be-gssapi.o
+endif
+
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 57c2f48..c0366fd 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -22,6 +22,7 @@
 #include <unistd.h>

 #include "libpq/auth.h"
+#include "libpq/libpq-be.h"
 #include "libpq/crypt.h"
 #include "libpq/ip.h"
 #include "libpq/libpq.h"
@@ -36,7 +37,7 @@
  * Global authentication functions
  *----------------------------------------------------------------
  */
-static void sendAuthRequest(Port *port, AuthRequest areq);
+void sendAuthRequest(Port *port, AuthRequest areq);
 static void auth_failed(Port *port, int status, char *logdetail);
 static char *recv_password_packet(Port *port);
 static int    recv_and_check_password_packet(Port *port, char **logdetail);
@@ -132,21 +133,6 @@ bool        pg_krb_caseins_users;


 /*----------------------------------------------------------------
- * GSSAPI Authentication
- *----------------------------------------------------------------
- */
-#ifdef ENABLE_GSS
-#if defined(HAVE_GSSAPI_H)
-#include <gssapi.h>
-#else
-#include <gssapi/gssapi.h>
-#endif
-
-static int    pg_GSS_recvauth(Port *port);
-#endif   /* ENABLE_GSS */
-
-
-/*----------------------------------------------------------------
  * SSPI Authentication
  *----------------------------------------------------------------
  */
@@ -167,22 +153,6 @@ static int    pg_SSPI_recvauth(Port *port);
 static int    CheckRADIUSAuth(Port *port);


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

-
-
-/*----------------------------------------------------------------
- * GSSAPI authentication system
- *----------------------------------------------------------------
- */
-#ifdef ENABLE_GSS
-
-#if defined(WIN32) && !defined(WIN32_ONLY_COMPILER)
-/*
- * MIT Kerberos GSSAPI DLL doesn't properly export the symbols for MingW
- * that contain the OIDs required. Redefine here, values copied
- * from src/athena/auth/krb5/src/lib/gssapi/generic/gssapi_generic.c
- */
-static const gss_OID_desc GSS_C_NT_USER_NAME_desc =
-{10, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x02"};
-static GSS_DLLIMP gss_OID GSS_C_NT_USER_NAME = &GSS_C_NT_USER_NAME_desc;
-#endif
-
-
-static void
-pg_GSS_error(int severity, char *errmsg, OM_uint32 maj_stat, OM_uint32 min_stat)
-{
-    gss_buffer_desc gmsg;
-    OM_uint32    lmin_s,
-                msg_ctx;
-    char        msg_major[128],
-                msg_minor[128];
-
-    /* Fetch major status message */
-    msg_ctx = 0;
-    gss_display_status(&lmin_s, maj_stat, GSS_C_GSS_CODE,
-                       GSS_C_NO_OID, &msg_ctx, &gmsg);
-    strlcpy(msg_major, gmsg.value, sizeof(msg_major));
-    gss_release_buffer(&lmin_s, &gmsg);
-
-    if (msg_ctx)
-
-        /*
-         * More than one message available. XXX: Should we loop and read all
-         * messages? (same below)
-         */
-        ereport(WARNING,
-                (errmsg_internal("incomplete GSS error report")));
-
-    /* Fetch mechanism minor status message */
-    msg_ctx = 0;
-    gss_display_status(&lmin_s, min_stat, GSS_C_MECH_CODE,
-                       GSS_C_NO_OID, &msg_ctx, &gmsg);
-    strlcpy(msg_minor, gmsg.value, sizeof(msg_minor));
-    gss_release_buffer(&lmin_s, &gmsg);
-
-    if (msg_ctx)
-        ereport(WARNING,
-                (errmsg_internal("incomplete GSS minor error report")));
-
-    /*
-     * errmsg_internal, since translation of the first part must be done
-     * before calling this function anyway.
-     */
-    ereport(severity,
-            (errmsg_internal("%s", errmsg),
-             errdetail_internal("%s: %s", msg_major, msg_minor)));
-}
-
-static int
-pg_GSS_recvauth(Port *port)
-{
-    OM_uint32    maj_stat,
-                min_stat,
-                lmin_s,
-                gflags;
-    int            mtype;
-    int            ret;
-    StringInfoData buf;
-    gss_buffer_desc gbuf;
-
-    /*
-     * GSS auth is not supported for protocol versions before 3, because it
-     * relies on the overall message length word to determine the GSS payload
-     * size in AuthenticationGSSContinue and PasswordMessage messages. (This
-     * is, in fact, a design error in our GSS support, because protocol
-     * messages are supposed to be parsable without relying on the length
-     * word; but it's not worth changing it now.)
-     */
-    if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
-        ereport(FATAL,
-                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                 errmsg("GSSAPI is not supported in protocol version 2")));
-
-    if (pg_krb_server_keyfile && strlen(pg_krb_server_keyfile) > 0)
-    {
-        /*
-         * Set default Kerberos keytab file for the Krb5 mechanism.
-         *
-         * setenv("KRB5_KTNAME", pg_krb_server_keyfile, 0); except setenv()
-         * not always available.
-         */
-        if (getenv("KRB5_KTNAME") == NULL)
-        {
-            size_t        kt_len = strlen(pg_krb_server_keyfile) + 14;
-            char       *kt_path = malloc(kt_len);
-
-            if (!kt_path ||
-                snprintf(kt_path, kt_len, "KRB5_KTNAME=%s",
-                         pg_krb_server_keyfile) != kt_len - 2 ||
-                putenv(kt_path) != 0)
-            {
-                ereport(LOG,
-                        (errcode(ERRCODE_OUT_OF_MEMORY),
-                         errmsg("out of memory")));
-                return STATUS_ERROR;
-            }
-        }
-    }
-
-    /*
-     * We accept any service principal that's present in our keytab. This
-     * increases interoperability between kerberos implementations that see
-     * for example case sensitivity differently, while not really opening up
-     * any vector of attack.
-     */
-    port->gss->cred = GSS_C_NO_CREDENTIAL;
-
-    /*
-     * Initialize sequence with an empty context
-     */
-    port->gss->ctx = GSS_C_NO_CONTEXT;
-
-    /*
-     * Loop through GSSAPI message exchange. This exchange can consist of
-     * multiple messags sent in both directions. First message is always from
-     * the client. All messages from client to server are password packets
-     * (type 'p').
-     */
-    do
-    {
-        pq_startmsgread();
-
-        CHECK_FOR_INTERRUPTS();
-
-        mtype = pq_getbyte();
-        if (mtype != 'p')
-        {
-            /* Only log error if client didn't disconnect. */
-            if (mtype != EOF)
-                ereport(COMMERROR,
-                        (errcode(ERRCODE_PROTOCOL_VIOLATION),
-                         errmsg("expected GSS response, got message type %d",
-                                mtype)));
-            return STATUS_ERROR;
-        }
-
-        /* Get the actual GSS token */
-        initStringInfo(&buf);
-        if (pq_getmessage(&buf, PG_MAX_AUTH_TOKEN_LENGTH))
-        {
-            /* EOF - pq_getmessage already logged error */
-            pfree(buf.data);
-            return STATUS_ERROR;
-        }
-
-        /* Map to GSSAPI style buffer */
-        gbuf.length = buf.len;
-        gbuf.value = buf.data;
-
-        elog(DEBUG4, "Processing received GSS token of length %u",
-             (unsigned int) gbuf.length);
-
-        maj_stat = gss_accept_sec_context(
-                                          &min_stat,
-                                          &port->gss->ctx,
-                                          port->gss->cred,
-                                          &gbuf,
-                                          GSS_C_NO_CHANNEL_BINDINGS,
-                                          &port->gss->name,
-                                          NULL,
-                                          &port->gss->outbuf,
-                                          &gflags,
-                                          NULL,
-                                          NULL);
-
-        /* gbuf no longer used */
-        pfree(buf.data);
-
-        elog(DEBUG5, "gss_accept_sec_context major: %d, "
-             "minor: %d, outlen: %u, outflags: %x",
-             maj_stat, min_stat,
-             (unsigned int) port->gss->outbuf.length, gflags);
-
-        CHECK_FOR_INTERRUPTS();
-
-        if (port->gss->outbuf.length != 0)
-        {
-            /*
-             * Negotiation generated data to be sent to the client.
-             */
-            elog(DEBUG4, "sending GSS response token of length %u",
-                 (unsigned int) port->gss->outbuf.length);
-
-            sendAuthRequest(port, AUTH_REQ_GSS_CONT);
-
-            gss_release_buffer(&lmin_s, &port->gss->outbuf);
-        }
-
-        if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
-        {
-            gss_delete_sec_context(&lmin_s, &port->gss->ctx, GSS_C_NO_BUFFER);
-            pg_GSS_error(ERROR,
-                       gettext_noop("accepting GSS security context failed"),
-                         maj_stat, min_stat);
-        }
-
-        if (maj_stat == GSS_S_CONTINUE_NEEDED)
-            elog(DEBUG4, "GSS continue needed");
-
-    } while (maj_stat == GSS_S_CONTINUE_NEEDED);
-
-    if (port->gss->cred != GSS_C_NO_CREDENTIAL)
-    {
-        /*
-         * Release service principal credentials
-         */
-        gss_release_cred(&min_stat, &port->gss->cred);
-    }
-
-    /*
-     * GSS_S_COMPLETE indicates that authentication is now complete.
-     *
-     * Get the name of the user that authenticated, and compare it to the pg
-     * username that was specified for the connection.
-     */
-    maj_stat = gss_display_name(&min_stat, port->gss->name, &gbuf, NULL);
-    if (maj_stat != GSS_S_COMPLETE)
-        pg_GSS_error(ERROR,
-                     gettext_noop("retrieving GSS user name failed"),
-                     maj_stat, min_stat);
-
-    /*
-     * Split the username at the realm separator
-     */
-    if (strchr(gbuf.value, '@'))
-    {
-        char       *cp = strchr(gbuf.value, '@');
-
-        /*
-         * If we are not going to include the realm in the username that is
-         * passed to the ident map, destructively modify it here to remove the
-         * realm. Then advance past the separator to check the realm.
-         */
-        if (!port->hba->include_realm)
-            *cp = '\0';
-        cp++;
-
-        if (port->hba->krb_realm != NULL && strlen(port->hba->krb_realm))
-        {
-            /*
-             * Match the realm part of the name first
-             */
-            if (pg_krb_caseins_users)
-                ret = pg_strcasecmp(port->hba->krb_realm, cp);
-            else
-                ret = strcmp(port->hba->krb_realm, cp);
-
-            if (ret)
-            {
-                /* GSS realm does not match */
-                elog(DEBUG2,
-                   "GSSAPI realm (%s) and configured realm (%s) don't match",
-                     cp, port->hba->krb_realm);
-                gss_release_buffer(&lmin_s, &gbuf);
-                return STATUS_ERROR;
-            }
-        }
-    }
-    else if (port->hba->krb_realm && strlen(port->hba->krb_realm))
-    {
-        elog(DEBUG2,
-             "GSSAPI did not return realm but realm matching was requested");
-
-        gss_release_buffer(&lmin_s, &gbuf);
-        return STATUS_ERROR;
-    }
-
-    ret = check_usermap(port->hba->usermap, port->user_name, gbuf.value,
-                        pg_krb_caseins_users);
-
-    gss_release_buffer(&lmin_s, &gbuf);
-
-    return ret;
-}
-#endif   /* ENABLE_GSS */
-
-
 /*----------------------------------------------------------------
  * SSPI authentication system
  *----------------------------------------------------------------
diff --git a/src/backend/libpq/be-gssapi.c b/src/backend/libpq/be-gssapi.c
new file mode 100644
index 0000000..d4d6c3a
--- /dev/null
+++ b/src/backend/libpq/be-gssapi.c
@@ -0,0 +1,583 @@
+/*-------------------------------------------------------------------------
+ *
+ * be-gssapi.c
+ *      GSSAPI authentication and encryption support
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *      src/backend/libpq/auth.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "libpq/libpq.h"
+#include "libpq/libpq-be.h"
+#include "libpq/pqformat.h"
+#include "miscadmin.h"
+
+#if defined(WIN32) && !defined(WIN32_ONLY_COMPILER)
+/*
+ * MIT Kerberos GSSAPI DLL doesn't properly export the symbols for MingW
+ * that contain the OIDs required. Redefine here, values copied
+ * from src/athena/auth/krb5/src/lib/gssapi/generic/gssapi_generic.c
+ */
+static const gss_OID_desc GSS_C_NT_USER_NAME_desc =
+{10, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x02"};
+static GSS_DLLIMP gss_OID GSS_C_NT_USER_NAME = &GSS_C_NT_USER_NAME_desc;
+#endif
+
+static void
+pg_GSS_error(int severity, char *errmsg, OM_uint32 maj_stat, OM_uint32 min_stat)
+{
+    gss_buffer_desc gmsg;
+    OM_uint32    lmin_s,
+                msg_ctx;
+    char        msg_major[128],
+                msg_minor[128];
+
+    /* Fetch major status message */
+    msg_ctx = 0;
+    gss_display_status(&lmin_s, maj_stat, GSS_C_GSS_CODE,
+                       GSS_C_NO_OID, &msg_ctx, &gmsg);
+    strlcpy(msg_major, gmsg.value, sizeof(msg_major));
+    gss_release_buffer(&lmin_s, &gmsg);
+
+    if (msg_ctx)
+
+        /*
+         * More than one message available. XXX: Should we loop and read all
+         * messages? (same below)
+         */
+        ereport(WARNING,
+                (errmsg_internal("incomplete GSS error report")));
+
+    /* Fetch mechanism minor status message */
+    msg_ctx = 0;
+    gss_display_status(&lmin_s, min_stat, GSS_C_MECH_CODE,
+                       GSS_C_NO_OID, &msg_ctx, &gmsg);
+    strlcpy(msg_minor, gmsg.value, sizeof(msg_minor));
+    gss_release_buffer(&lmin_s, &gmsg);
+
+    if (msg_ctx)
+        ereport(WARNING,
+                (errmsg_internal("incomplete GSS minor error report")));
+
+    /*
+     * errmsg_internal, since translation of the first part must be done
+     * before calling this function anyway.
+     */
+    ereport(severity,
+            (errmsg_internal("%s", errmsg),
+             errdetail_internal("%s: %s", msg_major, msg_minor)));
+}
+
+int
+pg_GSS_recvauth(Port *port)
+{
+    OM_uint32    maj_stat,
+                min_stat,
+                lmin_s,
+                gflags;
+    int            mtype;
+    int            ret;
+    StringInfoData buf;
+    gss_buffer_desc gbuf;
+
+    /*
+     * GSS auth is not supported for protocol versions before 3, because it
+     * relies on the overall message length word to determine the GSS payload
+     * size in AuthenticationGSSContinue and PasswordMessage messages. (This
+     * is, in fact, a design error in our GSS support, because protocol
+     * messages are supposed to be parsable without relying on the length
+     * word; but it's not worth changing it now.)
+     */
+    if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
+        ereport(FATAL,
+                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                 errmsg("GSSAPI is not supported in protocol version 2")));
+
+    if (pg_krb_server_keyfile && strlen(pg_krb_server_keyfile) > 0)
+    {
+        /*
+         * Set default Kerberos keytab file for the Krb5 mechanism.
+         *
+         * setenv("KRB5_KTNAME", pg_krb_server_keyfile, 0); except setenv()
+         * not always available.
+         */
+        if (getenv("KRB5_KTNAME") == NULL)
+        {
+            size_t        kt_len = strlen(pg_krb_server_keyfile) + 14;
+            char       *kt_path = malloc(kt_len);
+
+            if (!kt_path ||
+                snprintf(kt_path, kt_len, "KRB5_KTNAME=%s",
+                         pg_krb_server_keyfile) != kt_len - 2 ||
+                putenv(kt_path) != 0)
+            {
+                ereport(LOG,
+                        (errcode(ERRCODE_OUT_OF_MEMORY),
+                         errmsg("out of memory")));
+                return STATUS_ERROR;
+            }
+        }
+    }
+
+    /*
+     * We accept any service principal that's present in our keytab. This
+     * increases interoperability between kerberos implementations that see
+     * for example case sensitivity differently, while not really opening up
+     * any vector of attack.
+     */
+    port->gss->cred = GSS_C_NO_CREDENTIAL;
+
+    /*
+     * Initialize sequence with an empty context
+     */
+    port->gss->ctx = GSS_C_NO_CONTEXT;
+
+    /*
+     * Loop through GSSAPI message exchange. This exchange can consist of
+     * multiple messags sent in both directions. First message is always from
+     * the client. All messages from client to server are password packets
+     * (type 'p').
+     */
+    do
+    {
+        pq_startmsgread();
+
+        CHECK_FOR_INTERRUPTS();
+
+        mtype = pq_getbyte();
+        if (mtype != 'p')
+        {
+            /* Only log error if client didn't disconnect. */
+            if (mtype != EOF)
+                ereport(COMMERROR,
+                        (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                         errmsg("expected GSS response, got message type %d",
+                                mtype)));
+            return STATUS_ERROR;
+        }
+
+        /* Get the actual GSS token */
+        initStringInfo(&buf);
+        if (pq_getmessage(&buf, PG_MAX_AUTH_TOKEN_LENGTH))
+        {
+            /* EOF - pq_getmessage already logged error */
+            pfree(buf.data);
+            return STATUS_ERROR;
+        }
+
+        /* Map to GSSAPI style buffer */
+        gbuf.length = buf.len;
+        gbuf.value = buf.data;
+
+        elog(DEBUG4, "Processing received GSS token of length %u",
+             (unsigned int) gbuf.length);
+
+        maj_stat = gss_accept_sec_context(
+                                          &min_stat,
+                                          &port->gss->ctx,
+                                          port->gss->cred,
+                                          &gbuf,
+                                          GSS_C_NO_CHANNEL_BINDINGS,
+                                          &port->gss->name,
+                                          NULL,
+                                          &port->gss->outbuf,
+                                          &gflags,
+                                          NULL,
+                                          NULL);
+
+        /* gbuf no longer used */
+        pfree(buf.data);
+
+        elog(DEBUG5, "gss_accept_sec_context major: %d, "
+             "minor: %d, outlen: %u, outflags: %x",
+             maj_stat, min_stat,
+             (unsigned int) port->gss->outbuf.length, gflags);
+
+        CHECK_FOR_INTERRUPTS();
+
+        if (port->gss->outbuf.length != 0)
+        {
+            /*
+             * Negotiation generated data to be sent to the client.
+             */
+            elog(DEBUG4, "sending GSS response token of length %u",
+                 (unsigned int) port->gss->outbuf.length);
+
+            sendAuthRequest(port, AUTH_REQ_GSS_CONT);
+
+            gss_release_buffer(&lmin_s, &port->gss->outbuf);
+        }
+
+        if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
+        {
+            gss_delete_sec_context(&lmin_s, &port->gss->ctx, GSS_C_NO_BUFFER);
+            pg_GSS_error(ERROR,
+                       gettext_noop("accepting GSS security context failed"),
+                         maj_stat, min_stat);
+        }
+
+        if (maj_stat == GSS_S_CONTINUE_NEEDED)
+            elog(DEBUG4, "GSS continue needed");
+
+    } while (maj_stat == GSS_S_CONTINUE_NEEDED);
+
+    if (port->gss->cred != GSS_C_NO_CREDENTIAL)
+    {
+        /*
+         * Release service principal credentials
+         */
+        gss_release_cred(&min_stat, &port->gss->cred);
+    }
+
+    /*
+     * GSS_S_COMPLETE indicates that authentication is now complete.
+     *
+     * Get the name of the user that authenticated, and compare it to the pg
+     * username that was specified for the connection.
+     */
+    maj_stat = gss_display_name(&min_stat, port->gss->name, &gbuf, NULL);
+    if (maj_stat != GSS_S_COMPLETE)
+        pg_GSS_error(ERROR,
+                     gettext_noop("retrieving GSS user name failed"),
+                     maj_stat, min_stat);
+
+    /*
+     * Split the username at the realm separator
+     */
+    if (strchr(gbuf.value, '@'))
+    {
+        char       *cp = strchr(gbuf.value, '@');
+
+        /*
+         * If we are not going to include the realm in the username that is
+         * passed to the ident map, destructively modify it here to remove the
+         * realm. Then advance past the separator to check the realm.
+         */
+        if (!port->hba->include_realm)
+            *cp = '\0';
+        cp++;
+
+        if (port->hba->krb_realm != NULL && strlen(port->hba->krb_realm))
+        {
+            /*
+             * Match the realm part of the name first
+             */
+            if (pg_krb_caseins_users)
+                ret = pg_strcasecmp(port->hba->krb_realm, cp);
+            else
+                ret = strcmp(port->hba->krb_realm, cp);
+
+            if (ret)
+            {
+                /* GSS realm does not match */
+                elog(DEBUG2,
+                   "GSSAPI realm (%s) and configured realm (%s) don't match",
+                     cp, port->hba->krb_realm);
+                gss_release_buffer(&lmin_s, &gbuf);
+                return STATUS_ERROR;
+            }
+        }
+    }
+    else if (port->hba->krb_realm && strlen(port->hba->krb_realm))
+    {
+        elog(DEBUG2,
+             "GSSAPI did not return realm but realm matching was requested");
+
+        gss_release_buffer(&lmin_s, &gbuf);
+        return STATUS_ERROR;
+    }
+
+    ret = check_usermap(port->hba->usermap, port->user_name, gbuf.value,
+                        pg_krb_caseins_users);
+
+    gss_release_buffer(&lmin_s, &gbuf);
+
+    return ret;
+}
+
+static ssize_t
+be_gssapi_should_crypto(Port *port)
+{
+    OM_uint32 major, minor;
+    int open = 1;
+
+    if (port->gss->ctx == GSS_C_NO_CONTEXT)
+        return 0;
+    else if (port->gss->should_encrypt)
+        return 1;
+
+    major = gss_inquire_context(&minor, port->gss->ctx,
+                                NULL, NULL, NULL, NULL, NULL, NULL,
+                                &open);
+    if (major == GSS_S_NO_CONTEXT)
+    {
+        /*
+         * In MIT krb5 < 1.14, it was not possible to call gss_inquire_context
+         * on an incomplete context.  This was a violation of rfc2744 and has
+         * been corrected in https://github.com/krb5/krb5/pull/285
+         */
+        return 0;
+    }
+    else if (GSS_ERROR(major))
+    {
+        pg_GSS_error(ERROR,
+                     gettext_noop("GSSAPI context state error"),
+                     major, minor);
+        return -1;
+    }
+    else if (open != 0)
+    {
+        /*
+         * Though we can start encrypting here, our client is not ready since
+         * it has not received the final auth packet.  Set encryption on for
+         * the next packet, but send this one in the clear.
+         */
+        port->gss->should_encrypt = true;
+    }
+    return 0;
+}
+
+ssize_t
+be_gssapi_write(Port *port, void *ptr, size_t len)
+{
+    OM_uint32 major, minor;
+    gss_buffer_desc input, output;
+    ssize_t ret;
+    int conf;
+    uint32 netlen;
+    char lenbuf[4];
+    struct iovec iov[2];
+
+    ret = be_gssapi_should_crypto(port);
+    if (ret == -1)
+        return -1;
+    else if (ret == 0)
+        return secure_raw_write(port, ptr, len);
+
+    if (port->gss->writebuf.len != 0)
+    {
+        ret = send(port->sock,
+                   port->gss->writebuf.data + port->gss->writebuf.cursor,
+                   port->gss->writebuf.len - port->gss->writebuf.cursor,
+                   0);
+        if (ret < 0)
+            return ret;
+
+        port->gss->writebuf.cursor += ret;
+        if (port->gss->writebuf.cursor == port->gss->writebuf.len)
+        {
+            port->gss->writebuf.len = port->gss->writebuf.cursor = 0;
+            port->gss->writebuf.data[0] = '\0';
+            /* The entire request has now been written */
+            return len;
+        }
+        /* need to be called again */
+        return 0;
+    }
+
+    output.value = NULL;
+    output.length = 0;
+
+    input.value = ptr;
+    input.length = len;
+
+    conf = 0;
+    major = gss_wrap(&minor, port->gss->ctx, 1, GSS_C_QOP_DEFAULT,
+                     &input, &conf, &output);
+    if (GSS_ERROR(major))
+    {
+        pg_GSS_error(ERROR,
+                     gettext_noop("GSSAPI wrap error"),
+                     major, minor);
+        ret = -1;
+        goto cleanup;
+    }
+    else if (conf == 0)
+    {
+        ereport(FATAL, (errmsg("GSSAPI did not provide confidentiality")));
+        ret = -1;
+        goto cleanup;
+    }
+
+    netlen = htonl(output.length);
+    memcpy(lenbuf, &netlen, 4);
+    iov[0].iov_base = lenbuf;
+    iov[0].iov_len = 4;
+    iov[1].iov_base = output.value;
+    iov[1].iov_len = output.length;
+    ret = writev(port->sock, iov, 2);
+    if (ret == output.length + 4)
+    {
+        /*
+         * Strictly speaking, this isn't true; we did write more than `len`
+         * bytes.  However, this information is actually used to keep track of
+         * what has/hasn't been written yet, not actually report the number of
+         * bytes we wrote.
+         */
+        ret = len;
+        goto cleanup;
+    }
+    else if (ret < 0 && errno != EAGAIN && errno != EWOULDBLOCK)
+    {
+        ereport(FATAL, (errmsg("Failed to send entire GSSAPI blob")));
+        ret = -1;
+        goto cleanup;
+    }
+
+    if (ret < 4)
+    {
+        appendBinaryStringInfo(&port->gss->writebuf, lenbuf + ret, 4 - ret);
+        ret = 0;
+    }
+    else
+    {
+        ret -= 4;
+    }
+    appendBinaryStringInfo(&port->gss->writebuf, (char *)output.value + ret,
+                           output.length - ret);
+
+    /* Set return so that we get retried when the socket becomes writable */
+    ret = 0;
+ cleanup:
+    if (output.value != NULL)
+        gss_release_buffer(&minor, &output);
+
+    return ret;
+}
+
+static ssize_t
+be_gssapi_read_from_buffer(Port *port, void *ptr, size_t len)
+{
+    ssize_t ret = 0;
+
+    if (port->gss->buf.len > 4 && port->gss->buf.cursor < port->gss->buf.len)
+    {
+        if (len > port->gss->buf.len - port->gss->buf.cursor)
+            len = port->gss->buf.len - port->gss->buf.cursor;
+
+        memcpy(ptr, port->gss->buf.data + port->gss->buf.cursor, len);
+        port->gss->buf.cursor += len;
+
+        ret = len;
+    }
+
+    if (port->gss->buf.cursor == port->gss->buf.len)
+    {
+        port->gss->buf.cursor = port->gss->buf.len = 0;
+        port->gss->buf.data[0] = '\0';
+    }
+
+    return ret;
+}
+
+/*
+ * Here's how the buffering works:
+ *
+ * First, we read the packet into port->gss->buf.data.  The first four bytes
+ * of this will be the network-order length of the GSSAPI-encrypted blob; from
+ * position 4 to port->gss->buf.len is then this blob.  Therefore, at this
+ * point port->gss->buf.len is the length of the blob plus 4.
+ * port->gss->buf.cursor is zero for this entire step.
+ *
+ * Then we overwrite port->gss->buf.data entirely with the decrypted contents.
+ * At this point, port->gss->buf.len reflects the actual length of the
+ * decrypted data.  port->gss->buf.cursor is then used to incrementally return
+ * this data to the caller and is therefore nonzero during this step.
+ *
+ * Once all decrypted data is returned to the caller, the cycle repeats.
+ */
+ssize_t
+be_gssapi_read(Port *port, void *ptr, size_t len)
+{
+    OM_uint32 major, minor;
+    gss_buffer_desc input, output;
+    ssize_t ret;
+    int conf = 0;
+
+    ret = be_gssapi_should_crypto(port);
+    if (ret == -1)
+        return -1;
+    else if (ret == 0)
+        return secure_raw_read(port, ptr, len);
+
+    if (len == 0)
+        return 0;
+
+    if (port->gss->buf.cursor > 0)
+    {
+        ret = be_gssapi_read_from_buffer(port, ptr, len);
+        if (ret > 0)
+            return ret + be_gssapi_read(port, (char *)ptr + ret, len - ret);
+    }
+
+    /* our buffer is now empty */
+    if (port->gss->buf.len < 4)
+    {
+        enlargeStringInfo(&port->gss->buf, 4);
+        ret = secure_raw_read(port, port->gss->buf.data + port->gss->buf.len,
+                              4 - port->gss->buf.len);
+        if (ret < 0)
+            return ret;
+
+        port->gss->buf.len += ret;
+        port->gss->buf.data[port->gss->buf.len] = '\0';
+        if (port->gss->buf.len < 4)
+            return 0;
+    }
+
+    /* we know the length of the packet at this point */
+    memcpy((char *)&input.length, port->gss->buf.data, 4);
+    input.length = ntohl(input.length);
+    enlargeStringInfo(&port->gss->buf, input.length - port->gss->buf.len + 4);
+
+    ret = secure_raw_read(port, port->gss->buf.data + port->gss->buf.len,
+                          input.length - port->gss->buf.len + 4);
+    if (ret < 0)
+        return ret;
+
+    port->gss->buf.len += ret;
+    port->gss->buf.data[port->gss->buf.len] = '\0';
+    if (port->gss->buf.len - 4 < input.length)
+        return 0;
+
+    output.value = NULL;
+    output.length = 0;
+    input.value = port->gss->buf.data + 4;
+    major = gss_unwrap(&minor, port->gss->ctx, &input, &output, &conf, NULL);
+    if (GSS_ERROR(major))
+    {
+        pg_GSS_error(ERROR,
+                     gettext_noop("GSSAPI unwrap error"),
+                     major, minor);
+        ret = -1;
+        goto cleanup;
+    }
+    else if (conf == 0)
+    {
+        ereport(FATAL, (errmsg("GSSAPI did not provide confidentiality")));
+        ret = -1;
+        goto cleanup;
+    }
+
+    port->gss->buf.cursor = port->gss->buf.len = 0;
+    port->gss->buf.data[0] = '\0';
+    enlargeStringInfo(&port->gss->buf, output.length);
+    memcpy(port->gss->buf.data, output.value, output.length);
+    port->gss->buf.len = output.length;
+    port->gss->buf.data[port->gss->buf.len] = '\0';
+
+    ret = be_gssapi_read_from_buffer(port, ptr, len);
+ cleanup:
+    if (output.value != NULL)
+        gss_release_buffer(&minor, &output);
+
+    return ret;
+}
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index ac709d1..d43fa1b 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -132,6 +132,14 @@ retry:
     }
     else
 #endif
+#ifdef ENABLE_GSS
+    if (port->gss != NULL)
+    {
+        n = be_gssapi_read(port, ptr, len);
+        waitfor = WL_SOCKET_READABLE;
+    }
+    else
+#endif
     {
         n = secure_raw_read(port, ptr, len);
         waitfor = WL_SOCKET_READABLE;
@@ -234,6 +242,14 @@ retry:
     }
     else
 #endif
+#ifdef ENABLE_GSS
+    if (port->gss != NULL)
+    {
+        n = be_gssapi_write(port, ptr, len);
+        waitfor = WL_SOCKET_WRITEABLE;
+    }
+    else
+#endif
     {
         n = secure_raw_write(port, ptr, len);
         waitfor = WL_SOCKET_WRITEABLE;
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index b16fc28..d2f2a63 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -2355,6 +2355,10 @@ ConnCreate(int serverFd)
         ExitPostmaster(1);
     }
 #endif
+#ifdef ENABLE_GSS
+    initStringInfo(&port->gss->buf);
+    initStringInfo(&port->gss->writebuf);
+#endif
 #endif

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

diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 5d07b78..b0a31ae 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -68,6 +68,7 @@ typedef struct
 #include "datatype/timestamp.h"
 #include "libpq/hba.h"
 #include "libpq/pqcomm.h"
+#include "lib/stringinfo.h"


 typedef enum CAC_state
@@ -88,6 +89,9 @@ typedef struct
     gss_cred_id_t cred;            /* GSSAPI connection cred's */
     gss_ctx_id_t ctx;            /* GSSAPI connection context */
     gss_name_t    name;            /* GSSAPI client name */
+    StringInfoData buf;            /* GSSAPI encryption data buffering */
+    bool should_encrypt;        /* GSSAPI encryption start */
+    StringInfoData writebuf;    /* GSSAPI nonblocking write buffering */
 #endif
 } pg_gssinfo;
 #endif
@@ -214,6 +218,33 @@ extern void be_tls_get_cipher(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peerdn_name(Port *port, char *ptr, size_t len);
 #endif

+#ifdef ENABLE_GSS
+int pg_GSS_recvauth(Port *port);
+ssize_t be_gssapi_read(Port *port, void *ptr, size_t len);
+ssize_t be_gssapi_write(Port *port, void *ptr, size_t len);
+
+/* GUC */
+extern char       *pg_krb_server_keyfile;
+extern bool        pg_krb_caseins_users;
+
+/*
+ * Maximum accepted size of GSS and SSPI authentication tokens.
+ *
+ * Kerberos tickets are usually quite small, but the TGTs issued by Windows
+ * domain controllers include an authorization field known as the Privilege
+ * Attribute Certificate (PAC), which contains the user's Windows permissions
+ * (group memberships etc.). The PAC is copied into all tickets obtained on
+ * the basis of this TGT (even those issued by Unix realms which the Windows
+ * realm trusts), and can be several kB in size. The maximum token size
+ * accepted by Windows systems is determined by the MaxAuthToken Windows
+ * registry setting. Microsoft recommends that it is not set higher than
+ * 65535 bytes, so that seems like a reasonable limit for us as well.
+ */
+#define PG_MAX_AUTH_TOKEN_LENGTH       65535
+#endif
+
+extern void sendAuthRequest(Port *port, AuthRequest areq);
+
 extern ProtocolVersion FrontendProtocol;

 /* TCP keepalives configuration. These are no-ops on an AF_UNIX socket. */
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index 1b292d2..4b206b4 100644
--- a/src/interfaces/libpq/Makefile
+++ b/src/interfaces/libpq/Makefile
@@ -48,6 +48,10 @@ ifeq ($(with_openssl),yes)
 OBJS += fe-secure-openssl.o
 endif

+ifeq ($(with_gssapi),yes)
+OBJS += fe-gssapi.o
+endif
+
 ifeq ($(PORTNAME), cygwin)
 override shlib = cyg$(NAME)$(DLSUFFIX)
 endif
diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c
index cd863a5..c50f6dd 100644
--- a/src/interfaces/libpq/fe-auth.c
+++ b/src/interfaces/libpq/fe-auth.c
@@ -42,188 +42,6 @@
 #include "fe-auth.h"
 #include "libpq/md5.h"

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

+#ifdef ENABLE_GSS
+int pg_GSS_continue(PGconn *conn);
+int pg_GSS_startup(PGconn *conn);
+#endif
+
 #endif   /* FE_AUTH_H */
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 5ad4755..aa2340a 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -2798,6 +2798,10 @@ makeEmptyPGconn(void)
     conn->wait_ssl_try = false;
     conn->ssl_in_use = false;
 #endif
+#ifdef ENABLE_GSS
+    initPQExpBuffer(&conn->gbuf);
+    initPQExpBuffer(&conn->gwritebuf);
+#endif

     /*
      * We try to send at least 8K at a time, which is the usual size of pipe
@@ -2914,6 +2918,10 @@ freePGconn(PGconn *conn)
     if (conn->krbsrvname)
         free(conn->krbsrvname);
 #endif
+#if defined(ENABLE_GSS)
+    termPQExpBuffer(&conn->gbuf);
+    termPQExpBuffer(&conn->gwritebuf);
+#endif
 #if defined(ENABLE_GSS) && defined(ENABLE_SSPI)
     if (conn->gsslib)
         free(conn->gsslib);
@@ -3019,6 +3027,8 @@ closePGconn(PGconn *conn)
             gss_release_buffer(&min_s, &conn->ginbuf);
         if (conn->goutbuf.length)
             gss_release_buffer(&min_s, &conn->goutbuf);
+        resetPQExpBuffer(&conn->gbuf);
+        resetPQExpBuffer(&conn->gwritebuf);
     }
 #endif
 #ifdef ENABLE_SSPI
diff --git a/src/interfaces/libpq/fe-gssapi.c b/src/interfaces/libpq/fe-gssapi.c
new file mode 100644
index 0000000..471c6a2
--- /dev/null
+++ b/src/interfaces/libpq/fe-gssapi.c
@@ -0,0 +1,474 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-auth.c
+ *       The front-end (client) support for GSSAPI
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *      src/interfaces/libpq/fe-gssapi.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres_fe.h"
+
+#include "libpq-fe.h"
+#include "libpq-int.h"
+#include "fe-auth.h"
+
+#if defined(WIN32) && !defined(WIN32_ONLY_COMPILER)
+/*
+ * MIT Kerberos GSSAPI DLL doesn't properly export the symbols for MingW
+ * that contain the OIDs required. Redefine here, values copied
+ * from src/athena/auth/krb5/src/lib/gssapi/generic/gssapi_generic.c
+ */
+static const gss_OID_desc GSS_C_NT_HOSTBASED_SERVICE_desc =
+{10, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04"};
+static GSS_DLLIMP gss_OID GSS_C_NT_HOSTBASED_SERVICE = &GSS_C_NT_HOSTBASED_SERVICE_desc;
+#endif
+
+/*
+ * Fetch all errors of a specific type and append to "str".
+ */
+static void
+pg_GSS_error_int(PQExpBuffer str, const char *mprefix,
+                 OM_uint32 stat, int type)
+{
+    OM_uint32    lmin_s;
+    gss_buffer_desc lmsg;
+    OM_uint32    msg_ctx = 0;
+
+    do
+    {
+        gss_display_status(&lmin_s, stat, type,
+                           GSS_C_NO_OID, &msg_ctx, &lmsg);
+        appendPQExpBuffer(str, "%s: %s\n", mprefix, (char *) lmsg.value);
+        gss_release_buffer(&lmin_s, &lmsg);
+    } while (msg_ctx);
+}
+
+/*
+ * GSSAPI errors contain two parts; put both into conn->errorMessage.
+ */
+static void
+pg_GSS_error(const char *mprefix, PGconn *conn,
+             OM_uint32 maj_stat, OM_uint32 min_stat)
+{
+    resetPQExpBuffer(&conn->errorMessage);
+
+    /* Fetch major error codes */
+    pg_GSS_error_int(&conn->errorMessage, mprefix, maj_stat, GSS_C_GSS_CODE);
+
+    /* Add the minor codes as well */
+    pg_GSS_error_int(&conn->errorMessage, mprefix, min_stat, GSS_C_MECH_CODE);
+}
+
+/*
+ * Continue GSS authentication with next token as needed.
+ */
+int
+pg_GSS_continue(PGconn *conn)
+{
+    OM_uint32    maj_stat,
+                min_stat,
+                lmin_s;
+
+    maj_stat = gss_init_sec_context(&min_stat,
+                                    GSS_C_NO_CREDENTIAL,
+                                    &conn->gctx,
+                                    conn->gtarg_nam,
+                                    GSS_C_NO_OID,
+                                    GSS_C_MUTUAL_FLAG,
+                                    0,
+                                    GSS_C_NO_CHANNEL_BINDINGS,
+          (conn->gctx == GSS_C_NO_CONTEXT) ? GSS_C_NO_BUFFER : &conn->ginbuf,
+                                    NULL,
+                                    &conn->goutbuf,
+                                    NULL,
+                                    NULL);
+
+    if (conn->gctx != GSS_C_NO_CONTEXT)
+    {
+        free(conn->ginbuf.value);
+        conn->ginbuf.value = NULL;
+        conn->ginbuf.length = 0;
+    }
+
+    if (conn->goutbuf.length != 0)
+    {
+        /*
+         * GSS generated data to send to the server. We don't care if it's the
+         * first or subsequent packet, just send the same kind of password
+         * packet.
+         */
+        if (pqPacketSend(conn, 'p',
+                         conn->goutbuf.value, conn->goutbuf.length)
+            != STATUS_OK)
+        {
+            gss_release_buffer(&lmin_s, &conn->goutbuf);
+            return STATUS_ERROR;
+        }
+    }
+    gss_release_buffer(&lmin_s, &conn->goutbuf);
+
+    if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
+    {
+        pg_GSS_error(libpq_gettext("GSSAPI continuation error"),
+                     conn,
+                     maj_stat, min_stat);
+        gss_release_name(&lmin_s, &conn->gtarg_nam);
+        if (conn->gctx)
+            gss_delete_sec_context(&lmin_s, &conn->gctx, GSS_C_NO_BUFFER);
+        return STATUS_ERROR;
+    }
+
+    if (maj_stat == GSS_S_COMPLETE)
+        gss_release_name(&lmin_s, &conn->gtarg_nam);
+
+    return STATUS_OK;
+}
+
+/*
+ * Send initial GSS authentication token
+ */
+int
+pg_GSS_startup(PGconn *conn)
+{
+    OM_uint32    maj_stat,
+                min_stat;
+    int            maxlen;
+    gss_buffer_desc temp_gbuf;
+
+    if (!(conn->pghost && conn->pghost[0] != '\0'))
+    {
+        printfPQExpBuffer(&conn->errorMessage,
+                          libpq_gettext("host name must be specified\n"));
+        return STATUS_ERROR;
+    }
+
+    if (conn->gctx)
+    {
+        printfPQExpBuffer(&conn->errorMessage,
+                    libpq_gettext("duplicate GSS authentication request\n"));
+        return STATUS_ERROR;
+    }
+
+    /*
+     * Import service principal name so the proper ticket can be acquired by
+     * the GSSAPI system.
+     */
+    maxlen = NI_MAXHOST + strlen(conn->krbsrvname) + 2;
+    temp_gbuf.value = (char *) malloc(maxlen);
+    if (!temp_gbuf.value)
+    {
+        printfPQExpBuffer(&conn->errorMessage,
+                          libpq_gettext("out of memory\n"));
+        return STATUS_ERROR;
+    }
+    snprintf(temp_gbuf.value, maxlen, "%s@%s",
+             conn->krbsrvname, conn->pghost);
+    temp_gbuf.length = strlen(temp_gbuf.value);
+
+    maj_stat = gss_import_name(&min_stat, &temp_gbuf,
+                               GSS_C_NT_HOSTBASED_SERVICE, &conn->gtarg_nam);
+    free(temp_gbuf.value);
+
+    if (maj_stat != GSS_S_COMPLETE)
+    {
+        pg_GSS_error(libpq_gettext("GSSAPI name import error"),
+                     conn,
+                     maj_stat, min_stat);
+        return STATUS_ERROR;
+    }
+
+    /*
+     * Initial packet is the same as a continuation packet with no initial
+     * context.
+     */
+    conn->gctx = GSS_C_NO_CONTEXT;
+
+    return pg_GSS_continue(conn);
+}
+
+/*
+ * Only consider encryption when GSS context is complete
+ */
+static ssize_t
+pg_GSS_should_crypto(PGconn *conn)
+{
+    OM_uint32 major, minor;
+    int open = 1;
+
+    if (conn->gctx == GSS_C_NO_CONTEXT)
+        return 0;
+    else if (conn->gencrypt)
+        return 1;
+
+    major = gss_inquire_context(&minor, conn->gctx,
+                                NULL, NULL, NULL, NULL, NULL, NULL,
+                                &open);
+    if (major == GSS_S_NO_CONTEXT)
+    {
+        /*
+         * In MIT krb5 < 1.14, it was not possible to call gss_inquire_context
+         * on an incomplete context.  This was a violation of rfc2744 and has
+         * been corrected in https://github.com/krb5/krb5/pull/285
+         */
+        return 0;
+    }
+    else if (GSS_ERROR(major))
+    {
+        pg_GSS_error(libpq_gettext("GSSAPI context state error"), conn,
+                     major, minor);
+        return -1;
+    }
+    else if (open != 0)
+    {
+        conn->gencrypt = true;
+        return 1;
+    }
+    return 0;
+}
+
+ssize_t
+pg_GSS_write(PGconn *conn, void *ptr, size_t len)
+{
+    OM_uint32 major, minor;
+    gss_buffer_desc input, output;
+    ssize_t ret;
+    int conf;
+    uint32 netlen;
+    char lenbuf[4];
+    struct iovec iov[2];
+
+    ret = pg_GSS_should_crypto(conn);
+    if (ret == -1)
+        return -1;
+    else if (ret == 0)
+        return pqsecure_raw_write(conn, ptr, len);
+
+    if (conn->gwritebuf.len != 0)
+    {
+        ret = send(conn->sock, conn->gwritebuf.data + conn->gwritecurs,
+                   conn->gwritebuf.len - conn->gwritecurs, 0);
+        if (ret < 0)
+            return ret;
+        conn->gwritecurs += ret;
+        if (conn->gwritecurs == conn->gwritebuf.len)
+        {
+            conn->gwritebuf.len = conn->gwritecurs = 0;
+            conn->gwritebuf.data[0] = '\0';
+            /* The entire request has now been written */
+            return len;
+        }
+        /* need to be called again */
+        return 0;
+    }
+
+    output.value = NULL;
+    output.length = 0;
+
+    input.value = ptr;
+    input.length = len;
+
+    conf = 0;
+    major = gss_wrap(&minor, conn->gctx, 1, GSS_C_QOP_DEFAULT,
+                     &input, &conf, &output);
+    if (GSS_ERROR(major))
+    {
+        pg_GSS_error(libpq_gettext("GSSAPI wrap error"), conn,
+                     major, minor);
+        ret = -1;
+        goto cleanup;
+    }
+    else if (conf == 0)
+    {
+        printfPQExpBuffer(&conn->errorMessage, libpq_gettext(
+                              "GSSAPI did not provide confidentiality\n"));
+        ret = -1;
+        goto cleanup;
+    }
+
+    netlen = htonl(output.length);
+    memcpy(lenbuf, &netlen, 4);
+    iov[0].iov_base = lenbuf;
+    iov[0].iov_len = 4;
+    iov[1].iov_base = output.value;
+    iov[1].iov_len = output.length;
+    errno = 0;
+    ret = writev(conn->sock, iov, 2);
+    if (ret == output.length + 4)
+    {
+        /*
+         * pqsecure_write expects the return value, when >= 0, to be the
+         * number bytes from ptr delivered, not the number of bytes actually
+         * written to socket.
+         */
+        ret = len;
+        goto cleanup;
+    }
+    else if (ret < 0 && errno != EAGAIN && errno != EWOULDBLOCK)
+    {
+        printfPQExpBuffer(&conn->errorMessage, libpq_gettext(
+                              "GSSAPI writev() failed to send everything\n"));
+        ret = -1;
+        goto cleanup;
+    }
+
+    if (ret < 4)
+    {
+        appendBinaryPQExpBuffer(&conn->gwritebuf, lenbuf + ret, 4 - ret);
+        ret = 0;
+    }
+    else
+    {
+        ret -= 4;
+    }
+    appendBinaryPQExpBuffer(&conn->gwritebuf, (char *)output.value + 4 - ret,
+                            output.length + 4 - ret);
+
+    /* Set return so that we get retried when the socket becomes writable */
+    ret = 0;
+ cleanup:
+    if (output.value != NULL)
+        gss_release_buffer(&minor, &output);
+
+    return ret;
+}
+
+static ssize_t
+pg_GSS_read_from_buffer(PGconn *conn, void *ptr, size_t len)
+{
+    ssize_t ret = 0;
+
+    if (conn->gcursor < conn->gbuf.len)
+    {
+        if (len > conn->gbuf.len - conn->gcursor)
+            len = conn->gbuf.len - conn->gcursor;
+
+        memcpy(ptr, conn->gbuf.data + conn->gcursor, len);
+        conn->gcursor += len;
+        ret = len;
+    }
+
+    if (conn->gcursor == conn->gbuf.len)
+    {
+        conn->gcursor = conn->gbuf.len = 0;
+        conn->gbuf.data[0] = '\0';
+    }
+
+    return ret;
+}
+
+/*
+ * Buffering behaves as in be_gssapi_read (in be-gssapi.c).  Because this is
+ * the frontend, we use a PQExpBuffer at conn->gbuf instead of a StringInfo,
+ * and so there is an additional, separate cursor field in the structure.
+ */
+ssize_t
+pg_GSS_read(PGconn *conn, void *ptr, size_t len)
+{
+    OM_uint32 major, minor;
+    gss_buffer_desc input, output;
+    ssize_t ret;
+    int conf = 0;
+
+    ret = pg_GSS_should_crypto(conn);
+    if (ret == -1)
+        return -1;
+    else if (ret == 0)
+        return pqsecure_raw_read(conn, ptr, len);
+
+    if (len == 0)
+        return 0;
+
+    if (conn->gcursor > 0)
+    {
+        ret = pg_GSS_read_from_buffer(conn, ptr, len);
+        if (ret > 0)
+            return ret + pg_GSS_read(conn, (char *)ptr + ret, len - ret);
+    }
+
+    /* our buffer is now empty */
+    if (conn->gbuf.len < 4)
+    {
+        ret = enlargePQExpBuffer(&conn->gbuf, 4);
+        if (ret != 1)
+        {
+            printfPQExpBuffer(&conn->errorMessage, libpq_gettext(
+                                  "Failed to fit packet length in buffer\n"));
+            return -1;
+        }
+        ret = pqsecure_raw_read(conn, conn->gbuf.data, 4);
+        if (ret < 0)
+            /* error already set by secure_raw_read */
+            return ret;
+        conn->gbuf.len += ret;
+        conn->gbuf.data[conn->gbuf.len] = '\0';
+        if (conn->gbuf.len < 4)
+            return 0;
+    }
+
+    /* we know the length of the packet at this point */
+    memcpy((char *)&input.length, conn->gbuf.data, 4);
+    input.length = ntohl(input.length);
+    ret = enlargePQExpBuffer(&conn->gbuf, input.length - conn->gbuf.len + 4);
+    if (ret != 1)
+    {
+        printfPQExpBuffer(&conn->errorMessage, libpq_gettext(
+                              "GSSAPI encrypted packet (length %ld) too big\n"),
+                          input.length);
+        return -1;
+    }
+
+    ret = pqsecure_raw_read(conn, conn->gbuf.data + conn->gbuf.len,
+                            input.length - conn->gbuf.len + 4);
+    if (ret < 0)
+        return ret;
+    conn->gbuf.len += ret;
+    conn->gbuf.data[conn->gbuf.len] = '\0';
+    if (conn->gbuf.len - 4 < input.length)
+        return 0;
+
+    output.value = NULL;
+    output.length = 0;
+    input.value = conn->gbuf.data + 4;
+    major = gss_unwrap(&minor, conn->gctx, &input, &output, &conf, NULL);
+    if (GSS_ERROR(major))
+    {
+        pg_GSS_error(libpq_gettext("GSSAPI unwrap error"), conn,
+                     major, minor);
+        ret = -1;
+        goto cleanup;
+    }
+    else if (conf == 0)
+    {
+        printfPQExpBuffer(&conn->errorMessage, libpq_gettext(
+                              "GSSAPI did not provide confidentiality\n"));
+        ret = -1;
+        goto cleanup;
+    }
+
+    conn->gcursor = conn->gbuf.len = 0;
+    conn->gbuf.data[0] = '\0';
+    ret = enlargePQExpBuffer(&conn->gbuf, output.length);
+    if (ret != 1)
+    {
+        printfPQExpBuffer(&conn->errorMessage, libpq_gettext(
+                              "GSSAPI decrypted packet (length %ld) too big\n"),
+                          output.length);
+        return -1;
+    }
+    memcpy(conn->gbuf.data, output.value, output.length);
+    conn->gbuf.len = output.length;
+    conn->gbuf.data[conn->gbuf.len] = '\0';
+
+    ret = pg_GSS_read_from_buffer(conn, ptr, len);
+
+ cleanup:
+    if (output.value != NULL)
+        gss_release_buffer(&minor, &output);
+    return ret;
+}
diff --git a/src/interfaces/libpq/fe-secure.c b/src/interfaces/libpq/fe-secure.c
index 94e47a5..14fba1f 100644
--- a/src/interfaces/libpq/fe-secure.c
+++ b/src/interfaces/libpq/fe-secure.c
@@ -213,6 +213,13 @@ pqsecure_read(PGconn *conn, void *ptr, size_t len)
     }
     else
 #endif
+#ifdef ENABLE_GSS
+    if (conn->gctx != GSS_C_NO_CONTEXT)
+    {
+        n = pg_GSS_read(conn, ptr, len);
+    }
+    else
+#endif
     {
         n = pqsecure_raw_read(conn, ptr, len);
     }
@@ -279,7 +286,7 @@ pqsecure_raw_read(PGconn *conn, void *ptr, size_t len)
  * to determine whether to continue/retry after error.
  */
 ssize_t
-pqsecure_write(PGconn *conn, const void *ptr, size_t len)
+pqsecure_write(PGconn *conn, void *ptr, size_t len)
 {
     ssize_t        n;

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

 #include <time.h>
 #include <sys/types.h>
@@ -445,6 +446,11 @@ struct pg_conn
     gss_name_t    gtarg_nam;        /* GSS target name */
     gss_buffer_desc ginbuf;        /* GSS input token */
     gss_buffer_desc goutbuf;    /* GSS output token */
+    PQExpBufferData gbuf;        /* GSS encryption buffering */
+    size_t gcursor;                /* GSS buffering position */
+    bool gencrypt;                /* GSS is ready for encryption */
+    PQExpBufferData gwritebuf;    /* GSS nonblocking write buffering */
+    size_t gwritecurs;            /* GSS write buffer position */
 #endif

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

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

 /*
+ * The GSSAPI backend in fe-gssapi.c provides these functions.
+ */
+#ifdef ENABLE_GSS
+extern ssize_t pg_GSS_read(PGconn *conn, void *ptr, size_t len);
+extern ssize_t pg_GSS_write(PGconn *conn, void *ptr, size_t len);
+#endif
+
+/*
  * this is so that we can check if a connection is non-blocking internally
  * without the overhead of a function call
  */
--
2.7.0


Вложения

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

Предыдущее
От: Andres Freund
Дата:
Сообщение: Re: [PATCH] Code refactoring related to -fsanitize=use-after-scope
Следующее
От: Noah Misch
Дата:
Сообщение: Re: xlc atomics