[PATCH v1] GSSAPI encryption support

classic Classic list List threaded Threaded
219 messages Options
1234 ... 11
Reply | Threaded
Open this post in threaded view
|

[PATCH v1] GSSAPI encryption support

Robbie Harwood
Hello -hackers,

As previously discussed on this list, I have coded up GSSAPI encryption
support.  If it is easier for anyone, this code is also available for
viewing on my github:
https://github.com/postgres/postgres/compare/master...frozencemetery:feature/gssencrypt

Fallback support is present in both directions for talking to old client
and old servers; GSSAPI encryption is by default auto-upgraded to where
available (for compatibility), but both client and server contain
settings for requiring it.

There are 8 commits in this series; I have tried to err on the side of
creating too much separation rather than too little.  A patch for each
is attached.  This is v1 of the series.

Thanks!


From f506ba6ab6755f56c8aadba7d72a8839d5fbc0d9 Mon Sep 17 00:00:00 2001
From: "Robbie Harwood (frozencemetery)" <[hidden email]>
Date: Mon, 8 Jun 2015 19:27:45 -0400
Subject: build: Define with_gssapi for use in Makefiles

This is needed in order to control build of GSSAPI components.
---
 configure              | 2 ++
 configure.in           | 1 +
 src/Makefile.global.in | 1 +
 3 files changed, 4 insertions(+)

diff --git a/configure b/configure
index 0407c4f..b9bab06 100755
--- a/configure
+++ b/configure
@@ -711,6 +711,7 @@ with_uuid
 with_selinux
 with_openssl
 krb_srvtab
+with_gssapi
 with_python
 with_perl
 with_tcl
@@ -5452,6 +5453,7 @@ $as_echo "$with_gssapi" >&6; }
 
 
 
+
 #
 # Kerberos configuration parameters
 #
diff --git a/configure.in b/configure.in
index 1de41a2..113bd65 100644
--- a/configure.in
+++ b/configure.in
@@ -635,6 +635,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 c583b44..e50c87d 100644
--- a/src/Makefile.global.in
+++ b/src/Makefile.global.in
@@ -167,6 +167,7 @@ with_perl = @with_perl@
 with_python = @with_python@
 with_tcl = @with_tcl@
 with_openssl = @with_openssl@
+with_gssapi = @with_gssapi@
 with_selinux = @with_selinux@
 with_libxml = @with_libxml@
 with_libxslt = @with_libxslt@
--
2.1.4


From d5b973752968f87c9bb2ff9434d523657eb4ba67 Mon Sep 17 00:00:00 2001
From: "Robbie Harwood (frozencemetery)" <[hidden email]>
Date: Mon, 8 Jun 2015 20:16:42 -0400
Subject: client: Disable GSS encryption on old servers

---
 src/interfaces/libpq/fe-connect.c   | 34 ++++++++++++++++++++++++++++++++--
 src/interfaces/libpq/fe-protocol3.c |  5 +++++
 src/interfaces/libpq/libpq-int.h    |  1 +
 3 files changed, 38 insertions(+), 2 deletions(-)

diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index a45f4cb..c6c551a 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -91,8 +91,9 @@ static int ldapServiceLookup(const char *purl, PQconninfoOption *options,
  * application_name in a startup packet.  We hard-wire the value rather
  * than looking into errcodes.h since it reflects historical behavior
  * rather than that of the current code.
+ * Servers that do not support GSS encryption will also return this error.
  */
-#define ERRCODE_APPNAME_UNKNOWN "42704"
+#define ERRCODE_UNKNOWN_PARAM "42704"
 
 /* This is part of the protocol so just define it */
 #define ERRCODE_INVALID_PASSWORD "28P01"
@@ -2552,6 +2553,35 @@ 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)
+ {
+ /*
+ * We tried to request GSS encryption, but the server
+ * doesn't support it.  Hang up and try again.  A
+ * connection that doesn't support appname will also
+ * not support GSSAPI encryption, so this check goes
+ * before that check.  See comment below.
+ */
+ const char *sqlstate;
+
+ sqlstate = PQresultErrorField(res, PG_DIAG_SQLSTATE);
+ if (sqlstate &&
+ strcmp(sqlstate, ERRCODE_UNKNOWN_PARAM) == 0)
+ {
+ OM_uint32 minor;
+
+ PQclear(res);
+ conn->gss_disable_enc = true;
+ /* Must drop the old connection */
+ pqDropConnection(conn);
+ conn->status = CONNECTION_NEEDED;
+ gss_delete_sec_context(&minor, &conn->gctx,
+   GSS_C_NO_BUFFER);
+ goto keep_going;
+ }
+ }
+#endif
  else if (conn->send_appname &&
  (conn->appname || conn->fbappname))
  {
@@ -2569,7 +2599,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;
diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c
index a847f08..0deaa0f 100644
--- a/src/interfaces/libpq/fe-protocol3.c
+++ b/src/interfaces/libpq/fe-protocol3.c
@@ -2051,6 +2051,11 @@ build_startup_packet(const PGconn *conn, char *packet,
  if (conn->client_encoding_initial && conn->client_encoding_initial[0])
  ADD_STARTUP_OPTION("client_encoding", conn->client_encoding_initial);
 
+#ifdef ENABLE_GSS
+ if (!conn->gss_disable_enc)
+ ADD_STARTUP_OPTION("gss_encrypt", "on");
+#endif
+
  /* Add any environment-driven GUC settings needed */
  for (next_eo = options; next_eo->envName; next_eo++)
  {
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 2175957..1578d76 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -445,6 +445,7 @@ struct pg_conn
  gss_name_t gtarg_nam; /* GSS target name */
  gss_buffer_desc ginbuf; /* GSS input token */
  gss_buffer_desc goutbuf; /* GSS output token */
+ bool gss_disable_enc; /* Does server recognize gss_encrypt? */
 #endif
 
 #ifdef ENABLE_SSPI
--
2.1.4


From 36bd232742eb2b920d2cd88dd06176cde7e26cb2 Mon Sep 17 00:00:00 2001
From: "Robbie Harwood (frozencemetery)" <[hidden email]>
Date: Mon, 8 Jun 2015 21:20:23 -0400
Subject: client: GSSAPI encryption and decryption

---
 src/interfaces/libpq/Makefile        |  4 ++
 src/interfaces/libpq/fe-auth.c       |  2 +-
 src/interfaces/libpq/fe-auth.h       |  5 ++
 src/interfaces/libpq/fe-connect.c    |  5 ++
 src/interfaces/libpq/fe-misc.c       |  5 ++
 src/interfaces/libpq/fe-protocol3.c  | 27 +++++++++++
 src/interfaces/libpq/fe-secure-gss.c | 92 ++++++++++++++++++++++++++++++++++++
 src/interfaces/libpq/libpq-int.h     |  7 +++
 8 files changed, 146 insertions(+), 1 deletion(-)
 create mode 100644 src/interfaces/libpq/fe-secure-gss.c

diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index c2105f1..a9fb194 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-secure-gss.o
+endif
+
 ifeq ($(PORTNAME), cygwin)
 override shlib = cyg$(NAME)$(DLSUFFIX)
 endif
diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c
index 5891c75..af6dfff 100644
--- a/src/interfaces/libpq/fe-auth.c
+++ b/src/interfaces/libpq/fe-auth.c
@@ -82,7 +82,7 @@ pg_GSS_error_int(PQExpBuffer str, const char *mprefix,
 /*
  * GSSAPI errors contain two parts; put both into conn->errorMessage.
  */
-static void
+void
 pg_GSS_error(const char *mprefix, PGconn *conn,
  OM_uint32 maj_stat, OM_uint32 min_stat)
 {
diff --git a/src/interfaces/libpq/fe-auth.h b/src/interfaces/libpq/fe-auth.h
index 8d35767..5702a2d 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
+void pg_GSS_error(const char *mprefix, PGconn *conn, OM_uint32 maj_stat,
+  OM_uint32 min_stat);
+#endif
+
 #endif   /* FE_AUTH_H */
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index c6c551a..8fb0a90 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -2513,6 +2513,11 @@ keep_going: /* We will come back to here until there is
  /* We are done with authentication exchange */
  conn->status = CONNECTION_AUTH_OK;
 
+#ifdef ENABLE_GSS
+ if (conn->gctx != 0)
+ conn->gss_auth_done = true;
+#endif
+
  /*
  * Set asyncStatus so that PQgetResult will think that
  * what comes back next is the result of a query.  See
diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c
index 0dbcf73..2379aff 100644
--- a/src/interfaces/libpq/fe-misc.c
+++ b/src/interfaces/libpq/fe-misc.c
@@ -604,6 +604,11 @@ pqPutMsgEnd(PGconn *conn)
  memcpy(conn->outBuffer + conn->outMsgStart, &msgLen, 4);
  }
 
+#ifdef ENABLE_GSS
+ if (pggss_encrypt(conn) < 0)
+ return EOF;
+#endif
+
  /* Make message eligible to send */
  conn->outCount = conn->outMsgEnd;
 
diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c
index 0deaa0f..65acfd1 100644
--- a/src/interfaces/libpq/fe-protocol3.c
+++ b/src/interfaces/libpq/fe-protocol3.c
@@ -129,6 +129,33 @@ pqParseInput3(PGconn *conn)
  return;
  }
 
+#ifdef ENABLE_GSS
+ /* We want to be ready in both IDLE and BUSY states for encryption */
+ if (id == 'g')
+ {
+ ssize_t encEnd, next;
+
+ encEnd = pggss_inplace_decrypt(conn, msgLength);
+ if (encEnd <= 0)
+ {
+ /* error message placed by pggss_inplace_decrypt() */
+ pqSaveErrorResult(conn);
+ conn->asyncStatus = PGASYNC_READY;
+ pqDropConnection(conn);
+ conn->status = CONNECTION_BAD;
+ return;
+ }
+
+ /* shift contents of buffer to account for slack */
+ encEnd += conn->inStart;
+ next = conn->inStart + msgLength + 5;
+ memmove(conn->inBuffer + encEnd, conn->inBuffer + next,
+ conn->inEnd - next);
+ conn->inEnd = (conn->inEnd - next) + encEnd;
+ continue;
+ }
+#endif
+
  /*
  * NOTIFY and NOTICE messages can happen in any state; always process
  * them right away.
diff --git a/src/interfaces/libpq/fe-secure-gss.c b/src/interfaces/libpq/fe-secure-gss.c
new file mode 100644
index 0000000..afea9c3
--- /dev/null
+++ b/src/interfaces/libpq/fe-secure-gss.c
@@ -0,0 +1,92 @@
+#include <assert.h>
+
+#include "libpq-fe.h"
+#include "postgres_fe.h"
+#include "fe-auth.h"
+#include "libpq-int.h"
+
+ssize_t
+pggss_inplace_decrypt(PGconn *conn, int gsslen)
+{
+ OM_uint32 major, minor;
+ gss_buffer_desc input, output;
+ ssize_t n;
+ int conf;
+
+ input.length = gsslen;
+ input.value = conn->inBuffer + conn->inCursor;
+ output.length = 0;
+ output.value = NULL;
+
+ major = gss_unwrap(&minor, conn->gctx, &input, &output, &conf, NULL);
+ if (GSS_ERROR(major))
+ {
+ pg_GSS_error("GSSAPI unwrap error", conn, major, minor);
+ return -1;
+ }
+ else if (conf == 0)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+  libpq_gettext(
+  "received GSSAPI message without confidentiality\n"));
+ return -1;
+ }
+
+ memcpy(conn->inBuffer + conn->inStart, output.value, output.length);
+ n = output.length;
+ gss_release_buffer(&minor, &output);
+ return n;
+}
+
+int
+pggss_encrypt(PGconn *conn)
+{
+ OM_uint32 major, minor;
+ gss_buffer_desc input, output;
+ int msgLen, conf;
+ uint32 len_n;
+
+ if (conn->gss_disable_enc || !conn->gctx || !conn->gss_auth_done)
+ return 0;
+ assert(conn->outMsgStart > 0);
+
+ /* We need to encrypt message type as well */
+ conn->outMsgStart -= 1;
+ msgLen = conn->outMsgEnd - conn->outMsgStart;
+
+ input.value = conn->outBuffer + conn->outMsgStart;
+ input.length = msgLen;
+ output.length = 0;
+ output.value = NULL;
+
+ major = gss_wrap(&minor, conn->gctx, 1, GSS_C_QOP_DEFAULT, &input, &conf,
+ &output);
+ if (GSS_ERROR(major))
+ {
+ pg_GSS_error("GSSAPI wrap error", conn, major, minor);
+ return -1;
+ }
+ else if (conf == 0)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+  libpq_gettext(
+  "Failed to obtain confidentiality for outgoing GSSAPI message\n"));
+ return -1;
+ }
+
+ msgLen = output.length + 4;
+ if (pqCheckOutBufferSpace(conn->outMsgStart + msgLen + 1, conn))
+ return -1;
+
+ conn->outBuffer[conn->outMsgStart] = 'g'; /* GSSAPI message */
+
+ len_n = htonl(msgLen);
+ memcpy(conn->outBuffer + conn->outMsgStart + 1, &len_n, 4);
+
+ memcpy(conn->outBuffer + conn->outMsgStart + 1 + 4,
+ output.value, output.length);
+ conn->outMsgEnd = conn->outMsgStart + msgLen + 1;
+
+ gss_release_buffer(&minor, &output);
+ return msgLen + 1;
+}
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 1578d76..ff2e39d 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -446,6 +446,7 @@ struct pg_conn
  gss_buffer_desc ginbuf; /* GSS input token */
  gss_buffer_desc goutbuf; /* GSS output token */
  bool gss_disable_enc; /* Does server recognize gss_encrypt? */
+ bool gss_auth_done; /* Did we finish the AUTH step? */
 #endif
 
 #ifdef ENABLE_SSPI
@@ -643,6 +644,12 @@ extern bool pgtls_read_pending(PGconn *conn);
 extern ssize_t pgtls_write(PGconn *conn, const void *ptr, size_t len);
 
 /*
+ * GSSAPI encryption functions defined in fe-secure-gss.c
+ */
+extern ssize_t pggss_inplace_decrypt(PGconn *conn, int gsslen);
+extern int pggss_encrypt(PGconn *conn);
+
+/*
  * this is so that we can check if a connection is non-blocking internally
  * without the overhead of a function call
  */
--
2.1.4


From 0991d42173394e68e989d84e6574476d6a98e571 Mon Sep 17 00:00:00 2001
From: "Robbie Harwood (frozencemetery)" <[hidden email]>
Date: Tue, 9 Jun 2015 14:38:26 -0400
Subject: server: GSSAPI encryption and decryption

---
 src/backend/libpq/Makefile        |   4 ++
 src/backend/libpq/auth.c          |   2 +-
 src/backend/libpq/be-secure-gss.c | 101 ++++++++++++++++++++++++++++++++++++++
 src/backend/libpq/pqcomm.c        |  39 +++++++++++++++
 src/backend/tcop/postgres.c       |  18 ++++++-
 src/backend/utils/misc/guc.c      |  19 +++++++
 src/include/libpq/auth.h          |   5 ++
 src/include/libpq/libpq-be.h      |   9 ++++
 src/include/libpq/libpq.h         |   4 ++
 9 files changed, 199 insertions(+), 2 deletions(-)
 create mode 100644 src/backend/libpq/be-secure-gss.c

diff --git a/src/backend/libpq/Makefile b/src/backend/libpq/Makefile
index 09410c4..359e9d5 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-secure-gss.o
+endif
+
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 4699efa..913d356 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -728,7 +728,7 @@ static GSS_DLLIMP gss_OID GSS_C_NT_USER_NAME = &GSS_C_NT_USER_NAME_desc;
 #endif
 
 
-static void
+void
 pg_GSS_error(int severity, char *errmsg, OM_uint32 maj_stat, OM_uint32 min_stat)
 {
  gss_buffer_desc gmsg;
diff --git a/src/backend/libpq/be-secure-gss.c b/src/backend/libpq/be-secure-gss.c
new file mode 100644
index 0000000..64a4ed7
--- /dev/null
+++ b/src/backend/libpq/be-secure-gss.c
@@ -0,0 +1,101 @@
+#include <assert.h>
+
+#include "postgres.h"
+
+#include "libpq/libpq.h"
+#include "libpq/auth.h"
+#include "miscadmin.h"
+
+/* GUC value */
+bool gss_encrypt;
+
+size_t
+be_gss_encrypt(Port *port, char msgtype, const char **msgptr, size_t len)
+{
+ OM_uint32 major, minor;
+ gss_buffer_desc input, output;
+ uint32 len_n;
+ int conf;
+ char *ptr = *((char **)msgptr);
+ char *newbuf = palloc(len + 5);
+
+ len += 4;
+ len_n = htonl(len);
+
+ newbuf[0] = msgtype;
+ memcpy(newbuf + 1, &len_n, 4);
+ memcpy(newbuf + 5, ptr, len - 4);
+
+ input.length = len + 1; /* include type */
+ input.value = newbuf;
+ output.length = 0;
+ output.value = NULL;
+
+ major = gss_wrap(&minor, port->gss->ctx, 1, GSS_C_QOP_DEFAULT, &input,
+ &conf, &output);
+ if (GSS_ERROR(major))
+ {
+ pg_GSS_error(ERROR, gettext_noop("unwrapping GSS message failed"),
+ major, minor);
+ return -1;
+ }
+ assert(conf);
+
+ newbuf = repalloc(newbuf, output.length);
+ memcpy(newbuf, output.value, output.length);
+
+ len = output.length;
+ *msgptr = newbuf;
+ gss_release_buffer(&minor, &output);
+
+ return len;
+}
+
+int
+be_gss_inplace_decrypt(StringInfo inBuf)
+{
+ OM_uint32 major, minor;
+ gss_buffer_desc input, output;
+ int qtype, conf;
+ size_t msglen = 0;
+
+ input.length = inBuf->len;
+ input.value = inBuf->data;
+ output.length = 0;
+ output.value = NULL;
+
+ major = gss_unwrap(&minor, MyProcPort->gss->ctx, &input, &output,
+   &conf, NULL);
+ if (GSS_ERROR(major))
+ {
+ pg_GSS_error(ERROR, gettext_noop("wrapping GSS message failed"),
+ major, minor);
+ return -1;
+ }
+ else if (conf == 0)
+ {
+ ereport(COMMERROR,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("Expected GSSAPI confidentiality but it was not received")));
+ return -1;
+ }
+
+ qtype = ((char *)output.value)[0]; /* first byte is message type */
+ inBuf->len = output.length - 5; /* message starts */
+
+ memcpy((char *)&msglen, ((char *)output.value) + 1, 4);
+ msglen = ntohl(msglen);
+ if (msglen - 4 != inBuf->len)
+ {
+ ereport(COMMERROR,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("Length value inside GSSAPI-encrypted packet was malformed")));
+ return -1;
+ }
+
+ memcpy(inBuf->data, ((char *)output.value) + 5, inBuf->len);
+ inBuf->data[inBuf->len] = '\0'; /* invariant */
+ gss_release_buffer(&minor, &output);
+
+ return qtype;
+}
diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c
index a4b37ed..5a929a8 100644
--- a/src/backend/libpq/pqcomm.c
+++ b/src/backend/libpq/pqcomm.c
@@ -1485,6 +1485,19 @@ socket_putmessage(char msgtype, const char *s, size_t len)
 {
  if (DoingCopyOut || PqCommBusy)
  return 0;
+
+#ifdef ENABLE_GSS
+ /* Do not wrap auth requests. */
+ if (MyProcPort->hba->auth_method == uaGSS && gss_encrypt &&
+ msgtype != 'R' && msgtype != 'g')
+ {
+ len = be_gss_encrypt(MyProcPort, msgtype, &s, len);
+ if (len < 0)
+ goto fail;
+ msgtype = 'g';
+ }
+#endif
+
  PqCommBusy = true;
  if (msgtype)
  if (internal_putbytes(&msgtype, 1))
@@ -1500,10 +1513,20 @@ socket_putmessage(char msgtype, const char *s, size_t len)
  if (internal_putbytes(s, len))
  goto fail;
  PqCommBusy = false;
+#ifdef ENABLE_GSS
+ /* if we're GSSAPI encrypting, s was allocated in be_gss_encrypt */
+ if (msgtype == 'g')
+ pfree((char *)s);
+#endif
  return 0;
 
 fail:
  PqCommBusy = false;
+#ifdef ENABLE_GSS
+ /* if we're GSSAPI encrypting, s was allocated in be_gss_encrypt */
+ if (msgtype == 'g')
+ pfree((char *)s);
+#endif
  return EOF;
 }
 
@@ -1519,6 +1542,22 @@ socket_putmessage_noblock(char msgtype, const char *s, size_t len)
  int res PG_USED_FOR_ASSERTS_ONLY;
  int required;
 
+#ifdef ENABLE_GSS
+ /*
+ * Because socket_putmessage is also a front-facing function, we need the
+ * ability to GSSAPI encrypt from either.  Since socket_putmessage_noblock
+ * calls into socket_putmessage, socket_putmessage will handle freeing the
+ * allocated string.
+ */
+ if (gss_encrypt && msgtype != 'R' && msgtype != 'g')
+ {
+ len = be_gss_encrypt(MyProcPort, msgtype, &s, len);
+ if (len < 0)
+ return;
+ msgtype = 'g';
+ }
+#endif
+
  /*
  * Ensure we have enough space in the output buffer for the message header
  * as well as the message itself.
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index ce4bdaf..8510908 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -336,6 +336,7 @@ static int
 SocketBackend(StringInfo inBuf)
 {
  int qtype;
+ bool msg_got = false;
 
  /*
  * Get message type code from the frontend.
@@ -365,6 +366,21 @@ SocketBackend(StringInfo inBuf)
  return qtype;
  }
 
+#ifdef ENABLE_GSS
+ else if (qtype == 'g' && gss_encrypt &&
+ MyProcPort->hba->auth_method == uaGSS)
+ {
+ /* GSSAPI wrapping implies protocol >= 3 */
+ if (pq_getmessage(inBuf, 0))
+ return EOF;
+ msg_got = true;
+
+ qtype = be_gss_inplace_decrypt(inBuf);
+ if (qtype < 0)
+ return EOF;
+ }
+#endif
+
  /*
  * Validate message type code before trying to read body; if we have lost
  * sync, better to say "command unknown" than to run out of memory because
@@ -490,7 +506,7 @@ SocketBackend(StringInfo inBuf)
  * after the type code; we can read the message contents independently of
  * the type.
  */
- if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
+ if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3 && !msg_got)
  {
  if (pq_getmessage(inBuf, 0))
  return EOF; /* suitable message already logged */
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 0356ecb..a978af0 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -186,6 +186,10 @@ static const char *show_log_file_mode(void);
 static ConfigVariable *ProcessConfigFileInternal(GucContext context,
   bool applySettings, int elevel);
 
+#ifdef ENABLE_GSS
+static void assign_gss_encrypt(bool newval, void *extra);
+#endif
+
 
 /*
  * Options for enum values defined in this module.
@@ -1618,6 +1622,15 @@ static struct config_bool ConfigureNamesBool[] =
  NULL, NULL, NULL
  },
 
+ {
+ {"gss_encrypt", PGC_USERSET, CONN_AUTH_SECURITY,
+ gettext_noop("Whether client wants encryption for this connection."),
+ NULL,
+ GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
+ },
+ &gss_encrypt, false, NULL, assign_gss_encrypt, NULL
+ },
+
  /* End-of-list marker */
  {
  {NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL, NULL
@@ -10114,4 +10127,10 @@ show_log_file_mode(void)
  return buf;
 }
 
+static void
+assign_gss_encrypt(bool newval, void *extra)
+{
+ gss_encrypt = newval;
+}
+
 #include "guc-file.c"
diff --git a/src/include/libpq/auth.h b/src/include/libpq/auth.h
index 80f26a8..e98f560 100644
--- a/src/include/libpq/auth.h
+++ b/src/include/libpq/auth.h
@@ -26,4 +26,9 @@ extern void ClientAuthentication(Port *port);
 typedef void (*ClientAuthentication_hook_type) (Port *, int);
 extern PGDLLIMPORT ClientAuthentication_hook_type ClientAuthentication_hook;
 
+#ifdef ENABLE_GSS
+void pg_GSS_error(int severity, char *errmsg, OM_uint32 maj_stat,
+  OM_uint32 min_stat);
+#endif
+
 #endif   /* AUTH_H */
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 6171ef3..58712fc 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -30,6 +30,8 @@
 #endif
 
 #ifdef ENABLE_GSS
+#include "lib/stringinfo.h"
+
 #if defined(HAVE_GSSAPI_H)
 #include <gssapi.h>
 #else
@@ -219,6 +221,13 @@ extern void be_tls_get_cipher(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peerdn_name(Port *port, char *ptr, size_t len);
 #endif
 
+#ifdef ENABLE_GSS
+/* These functions are implemented in be-secure-gss.c */
+extern size_t
+be_gss_encrypt(Port *port, char msgtype, const char **msgptr, size_t len);
+extern int be_gss_inplace_decrypt(StringInfo inBuf);
+#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 c408e5b..e788cc8 100644
--- a/src/include/libpq/libpq.h
+++ b/src/include/libpq/libpq.h
@@ -99,4 +99,8 @@ extern char *SSLCipherSuites;
 extern char *SSLECDHCurve;
 extern bool SSLPreferServerCiphers;
 
+#ifdef ENABLE_GSS
+extern bool gss_encrypt;
+#endif
+
 #endif   /* LIBPQ_H */
--
2.1.4


From e55795e0638ca37447ef200f21f74db80b07ebf4 Mon Sep 17 00:00:00 2001
From: "Robbie Harwood (frozencemetery)" <[hidden email]>
Date: Fri, 12 Jun 2015 13:27:50 -0400
Subject: Error when receiving plaintext on GSS-encrypted connections

---
 src/backend/tcop/postgres.c         | 12 ++++++++++++
 src/interfaces/libpq/fe-protocol3.c | 32 ++++++++++++++++++++++++++++++--
 src/interfaces/libpq/libpq-int.h    |  1 +
 3 files changed, 43 insertions(+), 2 deletions(-)

diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 8510908..ba8ed4e 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -379,6 +379,18 @@ SocketBackend(StringInfo inBuf)
  if (qtype < 0)
  return EOF;
  }
+ else if (gss_encrypt && MyProcPort->hba->auth_method == uaGSS &&
+ qtype != 'g' && qtype != 'R' )
+ {
+ /*
+ * Either something malicious is occuring, or we have lost
+ * synchronization.
+ */
+ ereport(FATAL,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("invalid frontend message type %d", qtype)));
+ return EOF;
+ }
 #endif
 
  /*
diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c
index 65acfd1..d5fb461 100644
--- a/src/interfaces/libpq/fe-protocol3.c
+++ b/src/interfaces/libpq/fe-protocol3.c
@@ -131,7 +131,7 @@ pqParseInput3(PGconn *conn)
 
 #ifdef ENABLE_GSS
  /* We want to be ready in both IDLE and BUSY states for encryption */
- if (id == 'g')
+ if (id == 'g' && !conn->gss_disable_enc && conn->gctx)
  {
  ssize_t encEnd, next;
 
@@ -152,8 +152,33 @@ pqParseInput3(PGconn *conn)
  memmove(conn->inBuffer + encEnd, conn->inBuffer + next,
  conn->inEnd - next);
  conn->inEnd = (conn->inEnd - next) + encEnd;
- continue;
+
+ conn->inCursor = conn->inStart;
+ (void) pqGetc(&id, conn);
+ (void) pqGetInt(&msgLength, 4, conn);
+ msgLength -= 4;
+ if (msgLength != encEnd - conn->inCursor)
+ {
+ /* This isn't a sync error because decrypt was successful */
+ printfPQExpBuffer(&conn->errorMessage,
+  libpq_gettext(
+  "server lied about message length: got message length %ld, but expected legnth %d\n"),
+  encEnd - conn->inCursor, msgLength);
+ /* build an error result holding the error message */
+ pqSaveErrorResult(conn);
+ /* drop out of GetResult wait loop */
+ conn->asyncStatus = PGASYNC_READY;
+
+ pqDropConnection(conn);
+ /* No more connection to backend */
+ conn->status = CONNECTION_BAD;
+ }
+ conn->gss_decrypted_cur = true;
  }
+ else if (!conn->gss_disable_enc && conn->gss_auth_done &&
+ !conn->gss_decrypted_cur && id != 'E')
+ /* This could be a sync error, so let's handle it as such. */
+ handleSyncLoss(conn, id, msgLength);
 #endif
 
  /*
@@ -425,6 +450,9 @@ pqParseInput3(PGconn *conn)
  {
  /* Normal case: parsing agrees with specified length */
  conn->inStart = conn->inCursor;
+#ifdef ENABLE_GSS
+ conn->gss_decrypted_cur = false;
+#endif
  }
  else
  {
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index ff2e39d..7a3ebcd 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -447,6 +447,7 @@ struct pg_conn
  gss_buffer_desc goutbuf; /* GSS output token */
  bool gss_disable_enc; /* Does server recognize gss_encrypt? */
  bool gss_auth_done; /* Did we finish the AUTH step? */
+ bool gss_decrypted_cur; /* Is first message in buffer decrypted? */
 #endif
 
 #ifdef ENABLE_SSPI
--
2.1.4


From dcba977f9e37e80fafc7deb89b8856738f20928d Mon Sep 17 00:00:00 2001
From: "Robbie Harwood (frozencemetery)" <[hidden email]>
Date: Mon, 15 Jun 2015 16:52:10 -0400
Subject: server: hba option for requiring GSSAPI encryption

Also includes logic for failing clients that do not request encryption
in the startup packet when encryption is required.
---
 src/backend/libpq/hba.c           |  9 +++++++++
 src/backend/utils/init/postinit.c |  7 ++++++-
 src/backend/utils/misc/guc.c      | 12 +++++++++++-
 src/include/libpq/hba.h           |  1 +
 4 files changed, 27 insertions(+), 2 deletions(-)

diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 23c8b5d..90fe57f 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -1570,6 +1570,15 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
  else
  hbaline->include_realm = false;
  }
+ else if (strcmp(name, "require_encrypt") == 0)
+ {
+ if (hbaline->auth_method != uaGSS)
+ INVALID_AUTH_OPTION("require_encrypt", "gssapi");
+ if (strcmp(val, "1") == 0)
+ hbaline->require_encrypt = true;
+ else
+ hbaline->require_encrypt = false;
+ }
  else if (strcmp(name, "radiusserver") == 0)
  {
  struct addrinfo *gai_result;
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 0f04f28..cc5c9af 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"
@@ -1085,6 +1085,11 @@ process_startup_options(Port *port, bool am_superuser)
 
  SetConfigOption(name, value, gucctx, PGC_S_CLIENT);
  }
+
+ if (!gss_encrypt && port->hba->require_encrypt)
+ ereport(FATAL, (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
+ errmsg("GSSAPI encryption required from user \"%s\"",
+   port->user_name)));
 }
 
 /*
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index a978af0..3a8ba61 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -188,6 +188,7 @@ static ConfigVariable *ProcessConfigFileInternal(GucContext context,
 
 #ifdef ENABLE_GSS
 static void assign_gss_encrypt(bool newval, void *extra);
+static bool check_gss_encrypt(bool *newval, void **extra, GucSource source);
 #endif
 
 
@@ -1628,7 +1629,7 @@ static struct config_bool ConfigureNamesBool[] =
  NULL,
  GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
  },
- &gss_encrypt, false, NULL, assign_gss_encrypt, NULL
+ &gss_encrypt, false, check_gss_encrypt, assign_gss_encrypt, NULL
  },
 
  /* End-of-list marker */
@@ -10133,4 +10134,13 @@ assign_gss_encrypt(bool newval, void *extra)
  gss_encrypt = newval;
 }
 
+static bool
+check_gss_encrypt(bool *newval, void **extra, GucSource source)
+{
+ if (MyProcPort && MyProcPort->hba && MyProcPort->hba->require_encrypt &&
+ !*newval)
+ return false;
+ return true;
+}
+
 #include "guc-file.c"
diff --git a/src/include/libpq/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;
--
2.1.4


From b32190b69479a46291088b9ef15208d47975bcba Mon Sep 17 00:00:00 2001
From: "Robbie Harwood (frozencemetery)" <[hidden email]>
Date: Mon, 15 Jun 2015 19:54:29 -0400
Subject: client: gss_enc_require parameter to force GSS encryption

---
 src/interfaces/libpq/fe-connect.c | 27 ++++++++++++++++++++++-----
 src/interfaces/libpq/libpq-int.h  |  1 +
 2 files changed, 23 insertions(+), 5 deletions(-)

diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 8fb0a90..115a52c 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -297,6 +297,12 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
  offsetof(struct pg_conn, gsslib)},
 #endif
 
+#if defined(ENABLE_GSS)
+ {"gss_enc_require", "GSS_ENC_REQUIRE", "0", NULL,
+ "Require-GSS-encryption", "", 1, /* should be '0' or '1' */
+ offsetof(struct pg_conn, gss_enc_require)},
+#endif
+
  {"replication", NULL, NULL, NULL,
  "Replication", "D", 5,
  offsetof(struct pg_conn, replication)},
@@ -2559,14 +2565,16 @@ keep_going: /* We will come back to here until there is
  appendPQExpBufferStr(&conn->errorMessage,
  libpq_gettext("unexpected message from server during startup\n"));
 #ifdef ENABLE_GSS
- else if (!conn->gss_disable_enc)
+ else if (!conn->gss_disable_enc &&
+ *conn->gss_enc_require != '1')
  {
  /*
  * We tried to request GSS encryption, but the server
- * doesn't support it.  Hang up and try again.  A
- * connection that doesn't support appname will also
- * not support GSSAPI encryption, so this check goes
- * before that check.  See comment below.
+ * doesn't support it.  Retries are permitted here, so
+ * hang up and try again.  A connection that doesn't
+ * support appname will also not support GSSAPI
+ * encryption, so this check goes before that check.
+ * See comment below.
  */
  const char *sqlstate;
 
@@ -2614,6 +2622,15 @@ keep_going: /* We will come back to here until there is
  goto keep_going;
  }
  }
+#ifdef ENABLE_GSS
+ else if (*conn->gss_enc_require == '1')
+ /*
+ * It has been determined that appname was not the
+ * cause of connection failure, so give up.
+ */
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("Server does not support required GSS encryption\n"));
+#endif
 
  /*
  * if the resultStatus is FATAL, then conn->errorMessage
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 7a3ebcd..3140c7f 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -448,6 +448,7 @@ struct pg_conn
  bool gss_disable_enc; /* Does server recognize gss_encrypt? */
  bool gss_auth_done; /* Did we finish the AUTH step? */
  bool gss_decrypted_cur; /* Is first message in buffer decrypted? */
+ char *gss_enc_require; /* Can we downgrade to plaintext? */
 #endif
 
 #ifdef ENABLE_SSPI
--
2.1.4


From d5b374d0a466c4bf642d806168d973d4aad638e8 Mon Sep 17 00:00:00 2001
From: "Robbie Harwood (frozencemetery)" <[hidden email]>
Date: Mon, 29 Jun 2015 15:29:36 -0400
Subject: Document GSSAPI encryption

---
 doc/src/sgml/client-auth.sgml | 19 ++++++++++++++++---
 doc/src/sgml/libpq.sgml       | 12 ++++++++++++
 doc/src/sgml/protocol.sgml    | 40 ++++++++++++++++++++++++++++++++++++++++
 doc/src/sgml/runtime.sgml     | 20 +++++++++++++++++++-
 4 files changed, 87 insertions(+), 4 deletions(-)

diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml
index 5f72beb..3c49612 100644
--- a/doc/src/sgml/client-auth.sgml
+++ b/doc/src/sgml/client-auth.sgml
@@ -913,9 +913,10 @@ omicron         bryanh                  guest1
     <productname>GSSAPI</productname> with <productname>Kerberos</productname>
     authentication according to RFC 1964. <productname>GSSAPI</productname>
     provides automatic authentication (single sign-on) for systems
-    that support it. The authentication itself is secure, but the
-    data sent over the database connection will be sent unencrypted unless
-    <acronym>SSL</acronym> is used.
+    that support it. The authentication itself is secure, and GSSAPI can be
+    used for connection encryption as well (see the
+    <literal>require_encrypt</literal> parameter below); <acronym>SSL</acronym>
+    can also be used for connection security.
    </para>
 
    <para>
@@ -1046,6 +1047,18 @@ omicron         bryanh                  guest1
        </para>
       </listitem>
      </varlistentry>
+
+     <varlistentry>
+      <term><literal>require_encrypt</literal></term>
+      <listitem>
+       <para>
+        Whether to require GSSAPI encryption.  Default is off, which causes
+        GSSAPI encryption to be enabled if available and requested for
+        compatability with old clients.  It is recommended to set this unless
+        old clients are present.
+       </para>
+      </listitem>
+     </varlistentry>
     </variablelist>
    </para>
   </sect2>
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index de6b3ad..db2340c 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1356,6 +1356,18 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
       </listitem>
      </varlistentry>
 
+     <varlistentry id="libpq-connect-gss-enc-require" xreflabel="gss-enc-require">
+      <term><literal>gss_enc_require</literal></term>
+      <listitem>
+       <para>
+        If set, whether to require GSSAPI encryption support from the remote
+        server. Defaults to unset, which will cause the client to fall back to
+        not using GSSAPI encryption if the server does not support encryption
+        through GSSAPI.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="libpq-connect-service" xreflabel="service">
       <term><literal>service</literal></term>
       <listitem>
diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml
index 42e9497..8355e54 100644
--- a/doc/src/sgml/protocol.sgml
+++ b/doc/src/sgml/protocol.sgml
@@ -1295,6 +1295,46 @@
     of authentication checking.
    </para>
   </sect2>
+
+  <sect2>
+   <title><acronym>GSSAPI</acronym> Session Encryption</title>
+
+   <para>
+    If <productname>PostgreSQL</> was built with
+    <acronym>GSSAPI</acronym> and <acronym>GSSAPI</acronym> support, traffic
+    can also be encrypted using <acronym>GSSAPI</acronym>. To force encryption
+    using <acronym>GSSAPI</acronym>, set require_encrypt in
+    <filename>pg_hba.conf</filename>.
+   </para>
+
+   <para>
+    In order to probe for <acronym>GSSAPI</acronym> support, the client will
+    include in their StartupMessage the parameter gss_encrypt. If the server
+    does not support <acronym>GSSAPI</acronym> or <acronym>GSSAPI</acronym>
+    encryption, the server will error the connection; otherwise, it continues
+    as normal. The client may retry the connection
+    without <acronym>GSSAPI</acronym> encryption support depending on its
+    settings. If the client does not probe support, depending on settings
+    in <filename>pg_hba.conf</filename>, the server may drop
+    <acronym>GSSAPI</acronym>-authenticated connections without encryption.
+   </para>
+
+   <para>
+    If the client has probed <acronym>GSSAPI</acronym> encryption support and
+    the connection is <acronym>GSSAPI</acronym>-authenticated, then after the
+    server sends AuthenticationOk, all traffic between the client and server
+    will be <acronym>GSSAPI</acronym>-encrypted. Because
+    <acronym>GSSAPI</acronym> does not provide framing,
+    <acronym>GSSAPI</acronym>-encrypted messages are modeled after protocol-3
+    messages: the first byte is the caracter g, then four bytes of length, and
+    then an encrypted message.
+   </para>
+
+   <para>
+     It is valid to use <acronym>GSSAPI</acronym> encryption over
+     <acronym>SSL</acronym>-encrypted connections.
+   </para>
+  </sect2>
  </sect1>
 
 <sect1 id="protocol-replication">
diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml
index 547567e..0b1b009 100644
--- a/doc/src/sgml/runtime.sgml
+++ b/doc/src/sgml/runtime.sgml
@@ -1833,7 +1833,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
@@ -1843,6 +1843,15 @@ pg_dumpall -p 5432 | psql -d postgres -p 5433
    <literal>verify-full</> and have the appropriate root certificate
    file installed (<xref linkend="libq-ssl-certificates">).
   </para>
+
+  <para>
+   Another way of preventing spoofing on TCP connections is to use GSSAPI
+   encryption. In order to force all GSSAPI connections to be encrypted, one
+   should set <literal>require_encrypt</> in <filename>pg_hba.conf</> on GSS
+   connections. Then, using Kerberos, the client and server will mutually
+   authenticate, and the connection will be encrypted once the authentication
+   step is complete.
+  </para>
  </sect1>
 
  <sect1 id="encryption-options">
@@ -1958,6 +1967,15 @@ pg_dumpall -p 5432 | psql -d postgres -p 5433
       connect to servers only via SSL. <application>Stunnel</> or
       <application>SSH</> can also be used to encrypt transmissions.
      </para>
+
+     <para>
+      GSSAPI connections can also encrypt all data sent across the
+      network. In the <filename>pg_hba.conf</> file, the GSS authenticaion
+      method has a parameter to require encryption; otherwise, connections
+      will be encrypted if available and requiested by the client. On the
+      client side, there is also a parameter to require GSSAPI encryption
+      support from the server.
+     </para>
     </listitem>
   </varlistentry>
 
--
2.1.4


signature.asc (834 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH v1] GSSAPI encryption support

Stephen Frost
Robbie,

* Robbie Harwood ([hidden email]) wrote:

> As previously discussed on this list, I have coded up GSSAPI encryption
> support.  If it is easier for anyone, this code is also available for
> viewing on my github:
> https://github.com/postgres/postgres/compare/master...frozencemetery:feature/gssencrypt
>
> Fallback support is present in both directions for talking to old client
> and old servers; GSSAPI encryption is by default auto-upgraded to where
> available (for compatibility), but both client and server contain
> settings for requiring it.
>
> There are 8 commits in this series; I have tried to err on the side of
> creating too much separation rather than too little.  A patch for each
> is attached.  This is v1 of the series.
Excellent, thanks!  I've got other things to tend to at the moment, but
this is definitely something I'm interested in and will work on (likely
in August).

If we could get a reviewer or two to take a look and take the patch out
for a test-drive, at least, that would be a huge help.

        Thanks again!

                Stephen

signature.asc (836 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH v1] GSSAPI encryption support

Michael Paquier
In reply to this post by Robbie Harwood


On Fri, Jul 3, 2015 at 3:22 AM, Robbie Harwood <[hidden email]> wrote:
Hello -hackers,

As previously discussed on this list, I have coded up GSSAPI encryption
support.  If it is easier for anyone, this code is also available for
viewing on my github:
https://github.com/postgres/postgres/compare/master...frozencemetery:feature/gssencrypt

Fallback support is present in both directions for talking to old client
and old servers; GSSAPI encryption is by default auto-upgraded to where
available (for compatibility), but both client and server contain
settings for requiring it.

There are 8 commits in this series; I have tried to err on the side of
creating too much separation rather than too little.  A patch for each
is attached.  This is v1 of the series.

I just had a quick look at this patch, and here are some comments:
+   <para>
+    If the client has probed <acronym>GSSAPI</acronym> encryption support and
+    the connection is <acronym>GSSAPI</acronym>-authenticated, then after the
+    server sends AuthenticationOk, all traffic between the client and server
+    will be <acronym>GSSAPI</acronym>-encrypted. Because
+    <acronym>GSSAPI</acronym> does not provide framing,
+    <acronym>GSSAPI</acronym>-encrypted messages are modeled after protocol-3
+    messages: the first byte is the caracter g, then four bytes of length, and
+    then an encrypted message.
+   </para>
Message formats should be described in protocol.sgml in the section for message formats.

+      network. In the <filename>pg_hba.conf</> file, the GSS authenticaion
+      method has a parameter to require encryption; otherwise, connections
+      will be encrypted if available and requiested by the client. On the
s/authenticaion/authentication
s/requiested/requested

+        Whether to require GSSAPI encryption.  Default is off, which causes
+        GSSAPI encryption to be enabled if available and requested for
+        compatability with old clients.  It is recommended to set this unless
+        old clients are present.
s/compatability/compatibility

Going through the docs, the overall approach taken by the patch looks neat, and the default values as designed for both the client and the server are good things to do. Now actually looking at the code I am suspecting that some code portions could be largely simplified in the authentication protocol code, though I don't have the time yet to look at that in details.

Also, when trying to connect with GSSAPI, I found the following problem:
psql: lost synchronization with server: got message type "S", length 22
This happens whatever the value of require_encrypt on server-side is, either 0 or 1.
Regards,
--
Michael
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH v1] GSSAPI encryption support

Robbie Harwood
Michael Paquier <[hidden email]> writes:

> On Fri, Jul 3, 2015 at 3:22 AM, Robbie Harwood <[hidden email]> wrote:
>
>> There are 8 commits in this series; I have tried to err on the side of
>> creating too much separation rather than too little.  A patch for each
>> is attached.  This is v1 of the series.
>
> I just had a quick look at this patch, and here are some comments:

Hi!  Thanks for taking it for a spin.

> +   <para>
> +    If the client has probed <acronym>GSSAPI</acronym> encryption support and
> +    the connection is <acronym>GSSAPI</acronym>-authenticated, then after the
> +    server sends AuthenticationOk, all traffic between the client and server
> +    will be <acronym>GSSAPI</acronym>-encrypted. Because
> +    <acronym>GSSAPI</acronym> does not provide framing,
> +    <acronym>GSSAPI</acronym>-encrypted messages are modeled after protocol-3
> +    messages: the first byte is the caracter g, then four bytes of length, and
> +    then an encrypted message.
> +   </para>
>
> Message formats should be described in protocol.sgml in the section for
> message formats.
ACK.  In next version of patch.

> +      network. In the <filename>pg_hba.conf</> file, the GSS authenticaion
> +      method has a parameter to require encryption; otherwise, connections
> +      will be encrypted if available and requiested by the client. On the
> s/authenticaion/authentication
> s/requiested/requested
>
> +        Whether to require GSSAPI encryption.  Default is off, which causes
> +        GSSAPI encryption to be enabled if available and requested for
> +        compatability with old clients.  It is recommended to set this
> unless
> +        old clients are present.
> s/compatability/compatibility
Thanks for catching these.  They'll be included in a new version of the
series, which I'll post once you and I have resolved the issue at the
bottom.

> Going through the docs, the overall approach taken by the patch looks neat,
> and the default values as designed for both the client and the server are
> good things to do. Now actually looking at the code I am suspecting that
> some code portions could be largely simplified in the authentication
> protocol code, though I don't have the time yet to look at that in details.

If there are ways to make it simpler without sacrificing clarity, I
welcome them.  Fresh eyes could definitely help with that!

> Also, when trying to connect with GSSAPI, I found the following problem:
> psql: lost synchronization with server: got message type "S", length 22
> This happens whatever the value of require_encrypt on server-side is,
> either 0 or 1.

Well that's not good!  Since I'm not seeing this failure (even after
rebuilding my setup with patches applied to master), can you give me
more information here?  Since it's independent of require_encrypt, can
you verify it doesn't happen on master without my patches?  What
messages went over the wire to/from the server before this occurred (and
what was it trying to send at the time)?  Did you have valid
credentials?

signature.asc (834 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH v1] GSSAPI encryption support

Michael Paquier
On Sat, Aug 22, 2015 at 4:06 AM, Robbie Harwood wrote:

>
> Michael Paquier <[hidden email]> writes:
> > Going through the docs, the overall approach taken by the patch looks neat,
> > and the default values as designed for both the client and the server are
> > good things to do. Now actually looking at the code I am suspecting that
> > some code portions could be largely simplified in the authentication
> > protocol code, though I don't have the time yet to look at that in details.
>
> If there are ways to make it simpler without sacrificing clarity, I
> welcome them.  Fresh eyes could definitely help with that!

I'll look at that more at next week or the week after.

> > Also, when trying to connect with GSSAPI, I found the following problem:
> > psql: lost synchronization with server: got message type "S", length 22
> > This happens whatever the value of require_encrypt on server-side is,
> > either 0 or 1.
>
> Well that's not good!  Since I'm not seeing this failure (even after
> rebuilding my setup with patches applied to master), can you give me
> more information here?  Since it's independent of require_encrypt, can
> you verify it doesn't happen on master without my patches?

Well, I imagine that I have done nothing complicated... I have simply
set up a Kerberos KDC on a dev box, created necessary credentials on
this box in a keytab file that I have used afterwards to initialize a
Kerberos context with kinit for the psql client. On master things
worked fine, I was able to connect via gssapi. But with your patch the
communication protocol visibly lost track of the messages. I took a
memo about that, it's a bit rough, does not use pg_ident, but if that
can help:
http://michael.otacoo.com/manuals/postgresql/kerberos/

> What messages went over the wire to/from the server before this occurred (and
> what was it trying to send at the time)?

I haven't checked what were the messages sent over the network yet.

> Did you have valid credentials?

Yep. I just tried on master before switching to a build with your
patch that failed. After moving back to master things worked again.
--
Michael


--
Sent via pgsql-hackers mailing list ([hidden email])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH v2] GSSAPI encryption support

Robbie Harwood
In reply to this post by Michael Paquier
Michael Paquier <[hidden email]> writes:

> On Fri, Jul 3, 2015 at 3:22 AM, Robbie Harwood <[hidden email]> wrote:
>
>> Hello -hackers,
>>
>> As previously discussed on this list, I have coded up GSSAPI encryption
>> support.  If it is easier for anyone, this code is also available for
>> viewing on my github:
>>
>> https://github.com/postgres/postgres/compare/master...frozencemetery:feature/gssencrypt
>>
>> Fallback support is present in both directions for talking to old client
>> and old servers; GSSAPI encryption is by default auto-upgraded to where
>> available (for compatibility), but both client and server contain
>> settings for requiring it.
>>
>> There are 8 commits in this series; I have tried to err on the side of
>> creating too much separation rather than too little.  A patch for each
>> is attached.  This is v1 of the series.
>
> I just had a quick look at this patch, and here are some comments:
> +   <para>
> +    If the client has probed <acronym>GSSAPI</acronym> encryption support
> and
> +    the connection is <acronym>GSSAPI</acronym>-authenticated, then after
> the
> +    server sends AuthenticationOk, all traffic between the client and
> server
> +    will be <acronym>GSSAPI</acronym>-encrypted. Because
> +    <acronym>GSSAPI</acronym> does not provide framing,
> +    <acronym>GSSAPI</acronym>-encrypted messages are modeled after
> protocol-3
> +    messages: the first byte is the caracter g, then four bytes of length,
> and
> +    then an encrypted message.
> +   </para>
> Message formats should be described in protocol.sgml in the section for
> message formats.
>
> +      network. In the <filename>pg_hba.conf</> file, the GSS authenticaion
> +      method has a parameter to require encryption; otherwise, connections
> +      will be encrypted if available and requiested by the client. On the
> s/authenticaion/authentication
> s/requiested/requested
>
> +        Whether to require GSSAPI encryption.  Default is off, which causes
> +        GSSAPI encryption to be enabled if available and requested for
> +        compatability with old clients.  It is recommended to set this
> unless
> +        old clients are present.
> s/compatability/compatibility
As promised, here's a V2 to address your issues with comments.  I
haven't heard back on the issues you found in testing, so no other
changes are present.

This means that only the last patch has changed.  For convenience, I
will therefore only provide this new patch.  I have also updated the
version available from my github.

Thanks!

From 2e9017a572a3097fecf2f7e53bf5f9aabf6ae36d Mon Sep 17 00:00:00 2001
From: "Robbie Harwood (frozencemetery)" <[hidden email]>
Date: Mon, 29 Jun 2015 15:29:36 -0400
Subject: [PATCH] Document GSSAPI encryption

---
 doc/src/sgml/client-auth.sgml | 19 ++++++++--
 doc/src/sgml/libpq.sgml       | 12 +++++++
 doc/src/sgml/protocol.sgml    | 82 ++++++++++++++++++++++++++++++++++++++++++-
 doc/src/sgml/runtime.sgml     | 20 ++++++++++-
 4 files changed, 128 insertions(+), 5 deletions(-)

diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml
index ba04bdf..0863468 100644
--- a/doc/src/sgml/client-auth.sgml
+++ b/doc/src/sgml/client-auth.sgml
@@ -913,9 +913,10 @@ omicron         bryanh                  guest1
     <productname>GSSAPI</productname> with <productname>Kerberos</productname>
     authentication according to RFC 1964. <productname>GSSAPI</productname>
     provides automatic authentication (single sign-on) for systems
-    that support it. The authentication itself is secure, but the
-    data sent over the database connection will be sent unencrypted unless
-    <acronym>SSL</acronym> is used.
+    that support it. The authentication itself is secure, and GSSAPI can be
+    used for connection encryption as well (see the
+    <literal>require_encrypt</literal> parameter below); <acronym>SSL</acronym>
+    can also be used for connection security.
    </para>
 
    <para>
@@ -1046,6 +1047,18 @@ omicron         bryanh                  guest1
        </para>
       </listitem>
      </varlistentry>
+
+     <varlistentry>
+      <term><literal>require_encrypt</literal></term>
+      <listitem>
+       <para>
+        Whether to require GSSAPI encryption.  Default is off, which causes
+        GSSAPI encryption to be enabled if available and requested for
+        compatibility with old clients.  It is recommended to set this unless
+        old clients are present.
+       </para>
+      </listitem>
+     </varlistentry>
     </variablelist>
    </para>
   </sect2>
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 7940ef2..b80d29d 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1356,6 +1356,18 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
       </listitem>
      </varlistentry>
 
+     <varlistentry id="libpq-connect-gss-enc-require" xreflabel="gss-enc-require">
+      <term><literal>gss_enc_require</literal></term>
+      <listitem>
+       <para>
+        If set, whether to require GSSAPI encryption support from the remote
+        server. Defaults to unset, which will cause the client to fall back to
+        not using GSSAPI encryption if the server does not support encryption
+        through GSSAPI.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="libpq-connect-service" xreflabel="service">
       <term><literal>service</literal></term>
       <listitem>
diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml
index 42e9497..15e0014 100644
--- a/doc/src/sgml/protocol.sgml
+++ b/doc/src/sgml/protocol.sgml
@@ -1295,6 +1295,42 @@
     of authentication checking.
    </para>
   </sect2>
+
+  <sect2>
+   <title><acronym>GSSAPI</acronym> Session Encryption</title>
+
+   <para>
+    If <productname>PostgreSQL</> was built with
+    <acronym>GSSAPI</acronym> and <acronym>GSSAPI</acronym> support, traffic
+    can also be encrypted using <acronym>GSSAPI</acronym>. To force encryption
+    using <acronym>GSSAPI</acronym>, set require_encrypt in
+    <filename>pg_hba.conf</filename>.
+   </para>
+
+   <para>
+    In order to probe for <acronym>GSSAPI</acronym> support, the client will
+    include in their StartupMessage the parameter gss_encrypt. If the server
+    does not support <acronym>GSSAPI</acronym> or <acronym>GSSAPI</acronym>
+    encryption, the server will error the connection; otherwise, it continues
+    as normal. The client may retry the connection
+    without <acronym>GSSAPI</acronym> encryption support depending on its
+    settings. If the client does not probe support, depending on settings
+    in <filename>pg_hba.conf</filename>, the server may drop
+    <acronym>GSSAPI</acronym>-authenticated connections without encryption.
+   </para>
+
+   <para>
+    If the client has probed <acronym>GSSAPI</acronym> encryption support and
+    the connection is <acronym>GSSAPI</acronym>-authenticated, then after the
+    server sends AuthenticationOk, all traffic between the client and server
+    will be <acronym>GSSAPI</acronym>-encrypted.  See message formats above.
+   </para>
+
+   <para>
+     It is valid to use <acronym>GSSAPI</acronym> encryption over
+     <acronym>SSL</acronym>-encrypted connections.
+   </para>
+  </sect2>
  </sect1>
 
 <sect1 id="protocol-replication">
@@ -2191,7 +2227,9 @@ Notice that although each message includes a byte count at the beginning,
 the message format is defined so that the message end can be found without
 reference to the byte count.  This aids validity checking.  (The CopyData
 message is an exception, because it forms part of a data stream; the contents
-of any individual CopyData message cannot be interpretable on their own.)
+of any individual CopyData message cannot be interpretable on their own.  The
+GSSAPI message type is another exception because GSSAPI does not require
+payloads to include length information.)
 </para>
 
 <variablelist>
@@ -3921,6 +3959,48 @@ FunctionCallResponse (B)
 </listitem>
 </varlistentry>
 
+<varlistentry>
+<term>
+GSSAPI (F &amp; B)
+</term>
+<listitem>
+<para>
+<variablelist>
+<varlistentry>
+<term>
+        Byte1('g')
+</term>
+<listitem>
+<para>
+                Identifies the message as GSSAPI-encrypted data.
+</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>
+        Int32
+</term>
+<listitem>
+<para>
+                Length of message contents in bytes, including self.
+</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>
+        Byte<replaceable>n</replaceable>
+</term>
+<listitem>
+<para>
+                GSSAPI-encrypted data.  Once decrypted, will be in a valid
+                message format.
+</para>
+</listitem>
+</varlistentry>
+</variablelist>
+</para>
+</listitem>
+</varlistentry>
 
 <varlistentry>
 <term>
diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml
index 6d5b108..0d65fd6 100644
--- a/doc/src/sgml/runtime.sgml
+++ b/doc/src/sgml/runtime.sgml
@@ -1868,7 +1868,7 @@ pg_dumpall -p 5432 | psql -d postgres -p 5433
   </para>
 
   <para>
-   To prevent spoofing on TCP connections, the best solution is to use
+   To prevent spoofing on TCP connections, one possible solution is to use
    SSL certificates and make sure that clients check the server's certificate.
    To do that, the server
    must be configured to accept only <literal>hostssl</> connections (<xref
@@ -1878,6 +1878,15 @@ pg_dumpall -p 5432 | psql -d postgres -p 5433
    <literal>verify-full</> and have the appropriate root certificate
    file installed (<xref linkend="libq-ssl-certificates">).
   </para>
+
+  <para>
+   Another way of preventing spoofing on TCP connections is to use GSSAPI
+   encryption. In order to force all GSSAPI connections to be encrypted, one
+   should set <literal>require_encrypt</> in <filename>pg_hba.conf</> on GSS
+   connections. Then, using Kerberos, the client and server will mutually
+   authenticate, and the connection will be encrypted once the authentication
+   step is complete.
+  </para>
  </sect1>
 
  <sect1 id="encryption-options">
@@ -1993,6 +2002,15 @@ pg_dumpall -p 5432 | psql -d postgres -p 5433
       connect to servers only via SSL. <application>Stunnel</> or
       <application>SSH</> can also be used to encrypt transmissions.
      </para>
+
+     <para>
+      GSSAPI connections can also encrypt all data sent across the network. In
+      the <filename>pg_hba.conf</> file, the GSSAPI authentication method has
+      a parameter to require encryption; otherwise, connections will be
+      encrypted if available and requested by the client. On the client side,
+      there is also a parameter to require GSSAPI encryption support from the
+      server.
+     </para>
     </listitem>
   </varlistentry>
 
--
2.5.1


signature.asc (834 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH v2] GSSAPI encryption support

Michael Paquier
On Wed, Sep 9, 2015 at 4:12 AM, Robbie Harwood wrote:
> Michael Paquier writes:
> As promised, here's a V2 to address your issues with comments.  I
> haven't heard back on the issues you found in testing, so no other
> changes are present.

Well, the issue is still here: login through gssapi fails with your
patch, not with HEAD. This patch is next on my review list by the way
so I'll see what I can do about it soon even if I am in the US for
Postgres Open next week. Still, how did you test it? I am just
creating by myself a KDC, setting up a valid credential with kinit,
and after setting up Postgres for this purpose the protocol
communication just fails.

> This means that only the last patch has changed.  For convenience, I
> will therefore only provide this new patch.  I have also updated the
> version available from my github.

Thanks, this looks better. I have updated my local branch by replacing
the last patch of the previous series by this one, so I'll base any
potential hacking on this one.
Regards,
--
Michael


--
Sent via pgsql-hackers mailing list ([hidden email])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH v2] GSSAPI encryption support

Robbie Harwood
Michael Paquier <[hidden email]> writes:

> On Wed, Sep 9, 2015 at 4:12 AM, Robbie Harwood wrote:
>> Michael Paquier writes:
>> As promised, here's a V2 to address your issues with comments.  I
>> haven't heard back on the issues you found in testing, so no other
>> changes are present.
>
> Well, the issue is still here: login through gssapi fails with your
> patch, not with HEAD. This patch is next on my review list by the way
> so I'll see what I can do about it soon even if I am in the US for
> Postgres Open next week. Still, how did you test it? I am just
> creating by myself a KDC, setting up a valid credential with kinit,
> and after setting up Postgres for this purpose the protocol
> communication just fails.
My KDC is setup through freeIPA; I create a service for postgres,
acquire a keytab, set it in the config file, and fire up the server.  It
should go without saying that this is working for me, which is why I
asked you for more information so I could try to debug.  I wrote a post
on this back in June when this was still in development:
http://mivehind.net/page/view-page-slug/16/postgres-kerberos

signature.asc (834 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH v2] GSSAPI encryption support

Michael Paquier
On Thu, Sep 10, 2015 at 1:44 AM, Robbie Harwood <[hidden email]> wrote:

> Michael Paquier <[hidden email]> writes:
>
>> On Wed, Sep 9, 2015 at 4:12 AM, Robbie Harwood wrote:
>>> Michael Paquier writes:
>>> As promised, here's a V2 to address your issues with comments.  I
>>> haven't heard back on the issues you found in testing, so no other
>>> changes are present.
>>
>> Well, the issue is still here: login through gssapi fails with your
>> patch, not with HEAD. This patch is next on my review list by the way
>> so I'll see what I can do about it soon even if I am in the US for
>> Postgres Open next week. Still, how did you test it? I am just
>> creating by myself a KDC, setting up a valid credential with kinit,
>> and after setting up Postgres for this purpose the protocol
>> communication just fails.
>
> My KDC is setup through freeIPA; I create a service for postgres,
> acquire a keytab, set it in the config file, and fire up the server.  It
> should go without saying that this is working for me, which is why I
> asked you for more information so I could try to debug.  I wrote a post
> on this back in June when this was still in development:
> http://mivehind.net/page/view-page-slug/16/postgres-kerberos

Hm. OK. I'll give it a try with freeipa and your patch with Fedora for
example. Could you as well try the configuration I have used? In any
case, it seems to me that we have a real problem with your patch: the
gss authentication protocol is broken with your patch and *not* HEAD
when using a custom kdc like the one I have set up manually on one of
my VMs.
--
Michael


--
Sent via pgsql-hackers mailing list ([hidden email])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH v2] GSSAPI encryption support

Michael Paquier
On Thu, Sep 10, 2015 at 4:27 PM, Michael Paquier
<[hidden email]> wrote:

> On Thu, Sep 10, 2015 at 1:44 AM, Robbie Harwood <[hidden email]> wrote:
>> Michael Paquier <[hidden email]> writes:
>>
>>> On Wed, Sep 9, 2015 at 4:12 AM, Robbie Harwood wrote:
>>>> Michael Paquier writes:
>>>> As promised, here's a V2 to address your issues with comments.  I
>>>> haven't heard back on the issues you found in testing, so no other
>>>> changes are present.
>>>
>>> Well, the issue is still here: login through gssapi fails with your
>>> patch, not with HEAD. This patch is next on my review list by the way
>>> so I'll see what I can do about it soon even if I am in the US for
>>> Postgres Open next week. Still, how did you test it? I am just
>>> creating by myself a KDC, setting up a valid credential with kinit,
>>> and after setting up Postgres for this purpose the protocol
>>> communication just fails.
>>
>> My KDC is setup through freeIPA; I create a service for postgres,
>> acquire a keytab, set it in the config file, and fire up the server.  It
>> should go without saying that this is working for me, which is why I
>> asked you for more information so I could try to debug.  I wrote a post
>> on this back in June when this was still in development:
>> http://mivehind.net/page/view-page-slug/16/postgres-kerberos
> Hm. OK. I'll give it a try with freeipa and your patch with Fedora for
> example. Could you as well try the configuration I have used? In any
> case, it seems to me that we have a real problem with your patch: the
> gss authentication protocol is broken with your patch and *not* HEAD
> when using a custom kdc like the one I have set up manually on one of
> my VMs.

Looking more at this stuff. Your post assumes that you have an IPA
server available (I am not really familiar with this software stack)
already configured at hand so as you do not need to worry about any
low-level configuration and a KDC is provided as well, among other
things like ntpd or an apache instance. Well, the thing is that we
just need is a KDC for this patch to have an environment suitable for
testing, and some magic commands with kadmin.local, kinit, etc, and
not the whole set of features that an IPA server provides (when
kicking ipa-server-install one needs to provide a realm name, a KDC
admin password, so that's basically just a wrapper setting up
krb5.conf, which is handy when you want to have the full set in your
hands actually, though just to test this patch it does not seem worth
it). And I imagine that you do have an IPA server already set
facilitating your work.

Still, I gave it a try on a Fedora host, giving up after facing
several failures when trying to install the server. because of several
features.

Note that by duckduckging around I have bumped into some more
documentation to set up KDC with Postgres:
http://gpdb.docs.pivotal.io/4320/admin_guide/kerberos.html
This may be useful when testing this patch as well.
Regards,
--
Michael


--
Sent via pgsql-hackers mailing list ([hidden email])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH v2] GSSAPI encryption support

Robbie Harwood
Michael Paquier <[hidden email]> writes:

> On Thu, Sep 10, 2015 at 4:27 PM, Michael Paquier <[hidden email]> wrote:
>> On Thu, Sep 10, 2015 at 1:44 AM, Robbie Harwood <[hidden email]> wrote:
>>> Michael Paquier <[hidden email]> writes:
>>>> On Wed, Sep 9, 2015 at 4:12 AM, Robbie Harwood wrote:
>>>>> Michael Paquier writes:
>>>>> As promised, here's a V2 to address your issues with comments.  I
>>>>> haven't heard back on the issues you found in testing, so no other
>>>>> changes are present.
>>>>
>>>> Well, the issue is still here: login through gssapi fails with your
>>>> patch, not with HEAD. This patch is next on my review list by the
>>>> way so I'll see what I can do about it soon even if I am in the US
>>>> for Postgres Open next week. Still, how did you test it? I am just
>>>> creating by myself a KDC, setting up a valid credential with kinit,
>>>> and after setting up Postgres for this purpose the protocol
>>>> communication just fails.
>>>
>>> My KDC is setup through freeIPA; I create a service for postgres,
>>> acquire a keytab, set it in the config file, and fire up the server.
>>> It should go without saying that this is working for me, which is
>>> why I asked you for more information so I could try to debug.  I
>>> wrote a post on this back in June when this was still in
>>> development:
>>> http://mivehind.net/page/view-page-slug/16/postgres-kerberos
>>
>> Hm. OK. I'll give it a try with freeipa and your patch with Fedora
>> for example. Could you as well try the configuration I have used? In
>> any case, it seems to me that we have a real problem with your patch:
>> the gss authentication protocol is broken with your patch and *not*
>> HEAD when using a custom kdc like the one I have set up manually on
>> one of my VMs.
>
> Looking more at this stuff. Your post assumes that you have an IPA
> server available (I am not really familiar with this software stack)
> already configured at hand so as you do not need to worry about any
> low-level configuration and a KDC is provided as well, among other
> things like ntpd or an apache instance. Well, the thing is that we
> just need is a KDC for this patch to have an environment suitable for
> testing, and some magic commands with kadmin.local, kinit, etc, and
> not the whole set of features that an IPA server provides (when
> kicking ipa-server-install one needs to provide a realm name, a KDC
> admin password, so that's basically just a wrapper setting up
> krb5.conf, which is handy when you want to have the full set in your
> hands actually, though just to test this patch it does not seem worth
> it). And I imagine that you do have an IPA server already set
> facilitating your work.
>
> Still, I gave it a try on a Fedora host, giving up after facing
> several failures when trying to install the server. because of several
> features.
I'm sorry to hear that FreeIPA didn't work for you.  I'd be remiss if I
didn't suggest you file bugs against the project for things that are
broken, though that's somewhat orthogonal to the patchset at hand.

I gave your setup a try; I spun up a fc22 machine (krb5v1.13.2), and
installed the KDC as per your instructions.  I only did two things
differently: I'm using the default unit file for krb5kdc, and I'm using
the postgres base dir /var/lib/pgsql/data (this is a Fedora default and
I'm sure it doesn't matter for purposes of this).

I have no issues, no sync loss; nothing is amiss as far as I can see.
If there is actually a problem here, I need more information from you.
At the very least, as previously mentioned, I need to know what messages
went over the wire to/from the server before it occurred, and what
command (if it it made it to command processing) it was in the midst of
sending.

Thanks,
--Robbie

signature.asc (834 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH v2] GSSAPI encryption support

Robbie Harwood
Robbie Harwood <[hidden email]> writes:

>>>> Michael Paquier <[hidden email]> writes:
>>>>
>>>>> Well, the issue is still here: login through gssapi fails with
>>>>> your patch, not with HEAD. This patch is next on my review list by
>>>>> the way so I'll see what I can do about it soon even if I am in
>>>>> the US for Postgres Open next week. Still, how did you test it? I
>>>>> am just creating by myself a KDC, setting up a valid credential
>>>>> with kinit, and after setting up Postgres for this purpose the
>>>>> protocol communication just fails.
>
> I have no issues, no sync loss; nothing is amiss as far as I can see.
> If there is actually a problem here, I need more information from you.
> At the very least, as previously mentioned, I need to know what
> messages went over the wire to/from the server before it occurred, and
> what command (if it it made it to command processing) it was in the
> midst of sending.
Any follow-up on this?  I'd really like my code to be bug-free.

signature.asc (834 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH v1] GSSAPI encryption support

Andres Freund
In reply to this post by Robbie Harwood
Hi,

I quickly read through the patch, trying to understand what exactly is
happening here. To me the way the patch is split doesn't make much sense
- I don't mind incremental patches, but right now the steps don't
individually make sense.

On 2015-07-02 14:22:13 -0400, Robbie Harwood wrote:
> +#include <assert.h>

postgres.h should be the first header included.

> +size_t
> +be_gss_encrypt(Port *port, char msgtype, const char **msgptr, size_t len)
> +{
> + OM_uint32 major, minor;
> + gss_buffer_desc input, output;
> + uint32 len_n;
> + int conf;
> + char *ptr = *((char **)msgptr);

trivial nitpick, missing spaces...

> +int
> +be_gss_inplace_decrypt(StringInfo inBuf)
> +{
> + OM_uint32 major, minor;
> + gss_buffer_desc input, output;
> + int qtype, conf;
> + size_t msglen = 0;
> +
> + input.length = inBuf->len;
> + input.value = inBuf->data;
> + output.length = 0;
> + output.value = NULL;
> +
> + major = gss_unwrap(&minor, MyProcPort->gss->ctx, &input, &output,
> +   &conf, NULL);
> + if (GSS_ERROR(major))
> + {
> + pg_GSS_error(ERROR, gettext_noop("wrapping GSS message failed"),
> + major, minor);
> + return -1;
> + }
> + else if (conf == 0)
> + {
> + ereport(COMMERROR,
> + (errcode(ERRCODE_PROTOCOL_VIOLATION),
> + errmsg("Expected GSSAPI confidentiality but it was not received")));
> + return -1;
> + }

Hm. Aren't we leaking the gss buffer here?

> + qtype = ((char *)output.value)[0]; /* first byte is message type */
> + inBuf->len = output.length - 5; /* message starts */
> +
> + memcpy((char *)&msglen, ((char *)output.value) + 1, 4);
> + msglen = ntohl(msglen);
> + if (msglen - 4 != inBuf->len)
> + {
> + ereport(COMMERROR,
> + (errcode(ERRCODE_PROTOCOL_VIOLATION),
> + errmsg("Length value inside GSSAPI-encrypted packet was malformed")));
> + return -1;
> + }

and here?

Arguably it doesn't matter that much, since we'll usually disconnect
around here, but ...

> + memcpy(inBuf->data, ((char *)output.value) + 5, inBuf->len);
> + inBuf->data[inBuf->len] = '\0'; /* invariant */
> + gss_release_buffer(&minor, &output);
> +
> + return qtype;
> +}

> diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c
> index a4b37ed..5a929a8 100644
> --- a/src/backend/libpq/pqcomm.c
> +++ b/src/backend/libpq/pqcomm.c
> @@ -1485,6 +1485,19 @@ socket_putmessage(char msgtype, const char *s, size_t len)
>  {
>   if (DoingCopyOut || PqCommBusy)
>   return 0;
> +
> +#ifdef ENABLE_GSS
> + /* Do not wrap auth requests. */
> + if (MyProcPort->hba->auth_method == uaGSS && gss_encrypt &&
> + msgtype != 'R' && msgtype != 'g')
> + {
> + len = be_gss_encrypt(MyProcPort, msgtype, &s, len);
> + if (len < 0)
> + goto fail;
> + msgtype = 'g';
> + }
> +#endif

Putting encryption specific code here feels rather wrong to me.


> diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
> index 6171ef3..58712fc 100644
> --- a/src/include/libpq/libpq-be.h
> +++ b/src/include/libpq/libpq-be.h
> @@ -30,6 +30,8 @@
>  #endif
>  
>  #ifdef ENABLE_GSS
> +#include "lib/stringinfo.h"
> +

Conditionally including headers providing generic infrastructure strikes
me as a recipe for build failures in different configurations.

>  /* 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 c408e5b..e788cc8 100644
> --- a/src/include/libpq/libpq.h
> +++ b/src/include/libpq/libpq.h
> @@ -99,4 +99,8 @@ extern char *SSLCipherSuites;
>  extern char *SSLECDHCurve;
>  extern bool SSLPreferServerCiphers;
>  
> +#ifdef ENABLE_GSS
> +extern bool gss_encrypt;
> +#endif

> --- a/src/backend/utils/misc/guc.c
> +++ b/src/backend/utils/misc/guc.c
>  
> +#ifdef ENABLE_GSS
> +static void assign_gss_encrypt(bool newval, void *extra);
> +#endif
> +
>  
>  /*
>   * Options for enum values defined in this module.
> @@ -1618,6 +1622,15 @@ static struct config_bool ConfigureNamesBool[] =
>   NULL, NULL, NULL
>   },
>  
> + {
> + {"gss_encrypt", PGC_USERSET, CONN_AUTH_SECURITY,
> + gettext_noop("Whether client wants encryption for this connection."),
> + NULL,
> + GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
> + },
> + &gss_encrypt, false, NULL, assign_gss_encrypt, NULL
> + },
> +
>   /* End-of-list marker */
>   {
>   {NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL, NULL
> @@ -10114,4 +10127,10 @@ show_log_file_mode(void)
>   return buf;
>  }

The guc should always be present, not just when gss is built in. It
should error out when not supported. As is you're going to get linker
errors around gss_encrypt, assign_gss_encrypt.

> From e55795e0638ca37447ef200f21f74db80b07ebf4 Mon Sep 17 00:00:00 2001
> From: "Robbie Harwood (frozencemetery)" <[hidden email]>
> Date: Fri, 12 Jun 2015 13:27:50 -0400
> Subject: Error when receiving plaintext on GSS-encrypted connections

I don't see why this makes sense as a separate patch.

> Subject: server: hba option for requiring GSSAPI encryption
>
> Also includes logic for failing clients that do not request encryption
> in the startup packet when encryption is required.
> ---
>  src/backend/libpq/hba.c           |  9 +++++++++
>  src/backend/utils/init/postinit.c |  7 ++++++-
>  src/backend/utils/misc/guc.c      | 12 +++++++++++-
>  src/include/libpq/hba.h           |  1 +
>  4 files changed, 27 insertions(+), 2 deletions(-)
>
> diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
> index 23c8b5d..90fe57f 100644
> --- a/src/backend/libpq/hba.c
> +++ b/src/backend/libpq/hba.c
> @@ -1570,6 +1570,15 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
>   else
>   hbaline->include_realm = false;
>   }
> + else if (strcmp(name, "require_encrypt") == 0)
> + {
> + if (hbaline->auth_method != uaGSS)
> + INVALID_AUTH_OPTION("require_encrypt", "gssapi");
> + if (strcmp(val, "1") == 0)
> + hbaline->require_encrypt = true;
> + else
> + hbaline->require_encrypt = false;
> + }

So this is a new, undocumented, option that makes a connection require
encryption? But despite the generic name, it's gss specific?

> @@ -1628,7 +1629,7 @@ static struct config_bool ConfigureNamesBool[] =
>   NULL,
>   GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
>   },
> - &gss_encrypt, false, NULL, assign_gss_encrypt, NULL
> + &gss_encrypt, false, check_gss_encrypt, assign_gss_encrypt, NULL
>   },
>  
>   /* End-of-list marker */
> @@ -10133,4 +10134,13 @@ assign_gss_encrypt(bool newval, void *extra)
>   gss_encrypt = newval;
>  }
>  
> +static bool
> +check_gss_encrypt(bool *newval, void **extra, GucSource source)
> +{
> + if (MyProcPort && MyProcPort->hba && MyProcPort->hba->require_encrypt &&
> + !*newval)
> + return false;
> + return true;
> +}

Doing such checks in a guc assign hook seems like a horrible idea. Yes,
there's some precedent, but still.

Greetings,

Andres Freund


--
Sent via pgsql-hackers mailing list ([hidden email])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH v1] GSSAPI encryption support

Michael Paquier
On Sun, Oct 4, 2015 at 1:18 AM, Andres Freund <[hidden email]> wrote:
> Hi,
>
> I quickly read through the patch, trying to understand what exactly is
> happening here. To me the way the patch is split doesn't make much sense
> - I don't mind incremental patches, but right now the steps don't
> individually make sense.

I agree with Andres. While I looked a bit at this patch, I just had a
look at them a whole block and not individually.

> On 2015-07-02 14:22:13 -0400, Robbie Harwood wrote:
> [Andres' comments]

Here are some comments on top of what Andres has mentioned.

--- 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)

I think that using a new configure variable like that with a dedicated
file fe-secure-gss.c and be-secure-gss.c has little sense done this
way, and that it would be more adapted to get everything grouped in
fe-auth.c for the frontend and auth.c for the backend, or move all the
GSSAPI-related stuff in its own file. I can understand the move
though: this is to imitate OpenSSL in a way somewhat similar to what
has been done for it with a rather generic set if routines, but with
GSSAPI that's a bit different, we do not have such a set of routines,
hence based on this argument moving it to its own file has little
sense. Now, a move that would make sense though is to move all the
GSSAPI stuff in its own file, for example pg_GSS_recvauth and
pg_GSS_error for the backend, and you should do the same for the
frontend with all the pg_GSS_* routines. This should be as well a
refactoring patch on top of the actual feature.

diff --git a/src/interfaces/libpq/fe-secure-gss.c
b/src/interfaces/libpq/fe-secure-gss.c
new file mode 100644
index 0000000..afea9c3
--- /dev/null
+++ b/src/interfaces/libpq/fe-secure-gss.c
@@ -0,0 +1,92 @@
+#include <assert.h>
You should add a proper header to those new files.
--
Michael


--
Sent via pgsql-hackers mailing list ([hidden email])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH v1] GSSAPI encryption support

Robbie Harwood
In reply to this post by Andres Freund
Andres Freund <[hidden email]> writes:

> Hi,

Hi, thanks for the review; I really appreciate your time in going
through this.  I have questions about some of your comments, so I'll
wait a bit before sending a v3.  (By the way, there is a v2 of this I've
already posted, though you seem to have replied to the v1.  The only
difference is in documentation, though.)

> I quickly read through the patch, trying to understand what exactly is
> happening here. To me the way the patch is split doesn't make much sense
> - I don't mind incremental patches, but right now the steps don't
> individually make sense.

That's fair.  Can you suggest a better organization?

> On 2015-07-02 14:22:13 -0400, Robbie Harwood wrote:
>> +#include <assert.h>
>
> postgres.h should be the first header included.

Okay, will fix.

>> +size_t
>> +be_gss_encrypt(Port *port, char msgtype, const char **msgptr, size_t len)
>> +{
>> + OM_uint32 major, minor;
>> + gss_buffer_desc input, output;
>> + uint32 len_n;
>> + int conf;
>> + char *ptr = *((char **)msgptr);
>
> trivial nitpick, missing spaces...
Got it.

>> +int
>> +be_gss_inplace_decrypt(StringInfo inBuf)
>> +{
>> + OM_uint32 major, minor;
>> + gss_buffer_desc input, output;
>> + int qtype, conf;
>> + size_t msglen = 0;
>> +
>> + input.length = inBuf->len;
>> + input.value = inBuf->data;
>> + output.length = 0;
>> + output.value = NULL;
>> +
>> + major = gss_unwrap(&minor, MyProcPort->gss->ctx, &input, &output,
>> +   &conf, NULL);
>> + if (GSS_ERROR(major))
>> + {
>> + pg_GSS_error(ERROR, gettext_noop("wrapping GSS message failed"),
>> + major, minor);
>> + return -1;
>> + }
>> + else if (conf == 0)
>> + {
>> + ereport(COMMERROR,
>> + (errcode(ERRCODE_PROTOCOL_VIOLATION),
>> + errmsg("Expected GSSAPI confidentiality but it was not received")));
>> + return -1;
>> + }
>
> Hm. Aren't we leaking the gss buffer here?
>
>> + qtype = ((char *)output.value)[0]; /* first byte is message type */
>> + inBuf->len = output.length - 5; /* message starts */
>> +
>> + memcpy((char *)&msglen, ((char *)output.value) + 1, 4);
>> + msglen = ntohl(msglen);
>> + if (msglen - 4 != inBuf->len)
>> + {
>> + ereport(COMMERROR,
>> + (errcode(ERRCODE_PROTOCOL_VIOLATION),
>> + errmsg("Length value inside GSSAPI-encrypted packet was malformed")));
>> + return -1;
>> + }
>
> and here?
>
> Arguably it doesn't matter that much, since we'll usually disconnect
> around here, but ...
Probably better to be cautious about it.  Will fix.

>> diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c
>> index a4b37ed..5a929a8 100644
>> --- a/src/backend/libpq/pqcomm.c
>> +++ b/src/backend/libpq/pqcomm.c
>> @@ -1485,6 +1485,19 @@ socket_putmessage(char msgtype, const char *s, size_t len)
>>  {
>>   if (DoingCopyOut || PqCommBusy)
>>   return 0;
>> +
>> +#ifdef ENABLE_GSS
>> + /* Do not wrap auth requests. */
>> + if (MyProcPort->hba->auth_method == uaGSS && gss_encrypt &&
>> + msgtype != 'R' && msgtype != 'g')
>> + {
>> + len = be_gss_encrypt(MyProcPort, msgtype, &s, len);
>> + if (len < 0)
>> + goto fail;
>> + msgtype = 'g';
>> + }
>> +#endif
>
> Putting encryption specific code here feels rather wrong to me.
Do you have a suggestion about where this code *should* go?  I need to
filter on the message type since some can't be encrypted.  I was unable
to find another place, but I may have missed it.

>> diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
>> index 6171ef3..58712fc 100644
>> --- a/src/include/libpq/libpq-be.h
>> +++ b/src/include/libpq/libpq-be.h
>> @@ -30,6 +30,8 @@
>>  #endif
>>  
>>  #ifdef ENABLE_GSS
>> +#include "lib/stringinfo.h"
>> +
>
> Conditionally including headers providing generic infrastructure strikes
> me as a recipe for build failures in different configurations.
That's fair, will fix.

>>  /* 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 c408e5b..e788cc8 100644
>> --- a/src/include/libpq/libpq.h
>> +++ b/src/include/libpq/libpq.h
>> @@ -99,4 +99,8 @@ extern char *SSLCipherSuites;
>>  extern char *SSLECDHCurve;
>>  extern bool SSLPreferServerCiphers;
>>  
>> +#ifdef ENABLE_GSS
>> +extern bool gss_encrypt;
>> +#endif
>
>> --- a/src/backend/utils/misc/guc.c
>> +++ b/src/backend/utils/misc/guc.c
>>  
>> +#ifdef ENABLE_GSS
>> +static void assign_gss_encrypt(bool newval, void *extra);
>> +#endif
>> +
>>  
>>  /*
>>   * Options for enum values defined in this module.
>> @@ -1618,6 +1622,15 @@ static struct config_bool ConfigureNamesBool[] =
>>   NULL, NULL, NULL
>>   },
>>  
>> + {
>> + {"gss_encrypt", PGC_USERSET, CONN_AUTH_SECURITY,
>> + gettext_noop("Whether client wants encryption for this connection."),
>> + NULL,
>> + GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
>> + },
>> + &gss_encrypt, false, NULL, assign_gss_encrypt, NULL
>> + },
>> +
>>   /* End-of-list marker */
>>   {
>>   {NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL, NULL
>> @@ -10114,4 +10127,10 @@ show_log_file_mode(void)
>>   return buf;
>>  }
>
> The guc should always be present, not just when gss is built in. It
> should error out when not supported. As is you're going to get linker
> errors around gss_encrypt, assign_gss_encrypt.
If that is the style I will conform to it.

>> From e55795e0638ca37447ef200f21f74db80b07ebf4 Mon Sep 17 00:00:00 2001
>> From: "Robbie Harwood (frozencemetery)" <[hidden email]>
>> Date: Fri, 12 Jun 2015 13:27:50 -0400
>> Subject: Error when receiving plaintext on GSS-encrypted connections
>
> I don't see why this makes sense as a separate patch.

As previously stated, if there is another organization you prefer,
please suggest it.  As stated in my first email, I have attempted to err
on the side of having too many patches since splitting changesets later
is nontrivial, and squashing is easy.

>> Subject: server: hba option for requiring GSSAPI encryption
>>
>> Also includes logic for failing clients that do not request encryption
>> in the startup packet when encryption is required.
>> ---
>>  src/backend/libpq/hba.c           |  9 +++++++++
>>  src/backend/utils/init/postinit.c |  7 ++++++-
>>  src/backend/utils/misc/guc.c      | 12 +++++++++++-
>>  src/include/libpq/hba.h           |  1 +
>>  4 files changed, 27 insertions(+), 2 deletions(-)
>>
>> diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
>> index 23c8b5d..90fe57f 100644
>> --- a/src/backend/libpq/hba.c
>> +++ b/src/backend/libpq/hba.c
>> @@ -1570,6 +1570,15 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
>>   else
>>   hbaline->include_realm = false;
>>   }
>> + else if (strcmp(name, "require_encrypt") == 0)
>> + {
>> + if (hbaline->auth_method != uaGSS)
>> + INVALID_AUTH_OPTION("require_encrypt", "gssapi");
>> + if (strcmp(val, "1") == 0)
>> + hbaline->require_encrypt = true;
>> + else
>> + hbaline->require_encrypt = false;
>> + }
>
> So this is a new, undocumented, option that makes a connection require
> encryption? But despite the generic name, it's gss specific?
It was not my intent to leave it undocumented; I believe I documented it
as part of my changes.  If there is a place I have missed where it
should be documented, please tell me and I will happily document it there.

>> @@ -1628,7 +1629,7 @@ static struct config_bool ConfigureNamesBool[] =
>>   NULL,
>>   GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
>>   },
>> - &gss_encrypt, false, NULL, assign_gss_encrypt, NULL
>> + &gss_encrypt, false, check_gss_encrypt, assign_gss_encrypt, NULL
>>   },
>>  
>>   /* End-of-list marker */
>> @@ -10133,4 +10134,13 @@ assign_gss_encrypt(bool newval, void *extra)
>>   gss_encrypt = newval;
>>  }
>>  
>> +static bool
>> +check_gss_encrypt(bool *newval, void **extra, GucSource source)
>> +{
>> + if (MyProcPort && MyProcPort->hba && MyProcPort->hba->require_encrypt &&
>> + !*newval)
>> + return false;
>> + return true;
>> +}
>
> Doing such checks in a guc assign hook seems like a horrible idea. Yes,
> there's some precedent, but still.
Where would you prefer they go?  Isn't this what check hooks are for -
checking that it's valid to assign?

Thanks!
--Robbie

signature.asc (834 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH v1] GSSAPI encryption support

Robbie Harwood
In reply to this post by Michael Paquier
Michael Paquier <[hidden email]> writes:

> On Sun, Oct 4, 2015 at 1:18 AM, Andres Freund <[hidden email]> wrote:
>> Hi,
>>
>> I quickly read through the patch, trying to understand what exactly is
>> happening here. To me the way the patch is split doesn't make much sense
>> - I don't mind incremental patches, but right now the steps don't
>> individually make sense.
>
> I agree with Andres. While I looked a bit at this patch, I just had a
> look at them a whole block and not individually.
I'm hearing block from both of you!  Okay, if block is desired, I'll
squish for v3.  Sorry for the inconvenience.

>> On 2015-07-02 14:22:13 -0400, Robbie Harwood wrote:
>> [Andres' comments]
>
> Here are some comments on top of what Andres has mentioned.
>
> --- 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)
>
> I think that using a new configure variable like that with a dedicated
> file fe-secure-gss.c and be-secure-gss.c has little sense done this
> way, and that it would be more adapted to get everything grouped in
> fe-auth.c for the frontend and auth.c for the backend, or move all the
> GSSAPI-related stuff in its own file. I can understand the move
> though: this is to imitate OpenSSL in a way somewhat similar to what
> has been done for it with a rather generic set if routines, but with
> GSSAPI that's a bit different, we do not have such a set of routines,
> hence based on this argument moving it to its own file has little
> sense. Now, a move that would make sense though is to move all the
> GSSAPI stuff in its own file, for example pg_GSS_recvauth and
> pg_GSS_error for the backend, and you should do the same for the
> frontend with all the pg_GSS_* routines. This should be as well a
> refactoring patch on top of the actual feature.
My understanding is that frontend and backend code need to be separate
(for linking), so it's automatically in two places.  I really don't want
to put encryption-related code in files called "auth.c" and "fe-auth.c"
since those files are presumably for authentication, not encryption.

I'm not sure what you mean about "rather generic set if routines";
GSSAPI is a RFC-standardized interface.  I think I also don't understand
the last half of your above paragraph.

> diff --git a/src/interfaces/libpq/fe-secure-gss.c
> b/src/interfaces/libpq/fe-secure-gss.c
> new file mode 100644
> index 0000000..afea9c3
> --- /dev/null
> +++ b/src/interfaces/libpq/fe-secure-gss.c
> @@ -0,0 +1,92 @@
> +#include <assert.h>
> You should add a proper header to those new files.

Sorry, what?

signature.asc (834 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH v1] GSSAPI encryption support

Michael Paquier
On Sat, Oct 10, 2015 at 3:10 AM, Robbie Harwood wrote:

> Michael Paquier writes:
>>> On 2015-07-02 14:22:13 -0400, Robbie Harwood wrote:
>>> [Andres' comments]
>>
>> Here are some comments on top of what Andres has mentioned.
>>
>> --- 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)
>>
>> I think that using a new configure variable like that with a dedicated
>> file fe-secure-gss.c and be-secure-gss.c has little sense done this
>> way, and that it would be more adapted to get everything grouped in
>> fe-auth.c for the frontend and auth.c for the backend, or move all the
>> GSSAPI-related stuff in its own file. I can understand the move
>> though: this is to imitate OpenSSL in a way somewhat similar to what
>> has been done for it with a rather generic set if routines, but with
>> GSSAPI that's a bit different, we do not have such a set of routines,
>> hence based on this argument moving it to its own file has little
>> sense. Now, a move that would make sense though is to move all the
>> GSSAPI stuff in its own file, for example pg_GSS_recvauth and
>> pg_GSS_error for the backend, and you should do the same for the
>> frontend with all the pg_GSS_* routines. This should be as well a
>> refactoring patch on top of the actual feature.
>
> My understanding is that frontend and backend code need to be separate
> (for linking), so it's automatically in two places. I really don't want
> to put encryption-related code in files called "auth.c" and "fe-auth.c"
> since those files are presumably for authentication, not encryption.
>
> I'm not sure what you mean about "rather generic set if routines";
> GSSAPI is a RFC-standardized interface.  I think I also don't understand
> the last half of your above paragraph.

src/interfaces/libpq/fe-auth.c contains the following set of routines
related to GSS (frontend code in libpq):
- pg_GSS_error_int
- pg_GSS_error
- pg_GSS_continue
- pg_GSS_startup
src/backend/libpq/auth.c contains the following routines related to
GSS (backend code):
- pg_GSS_recvauth
- pg_GSS_error
My point would be simply to move all those routines in two new files
dedicated to GSS, then add your new routines for encryption in it.
Still, the only reason why the OpenSSL routines have been moved out of
be-secure.c to be-secure-openssl.c is to allow other libraries to be
plugged into that, the primary target being SChannel on Windows. And
that's not the case of GSS, so I think that the separation done as in
your patch is not adapted.

>> diff --git a/src/interfaces/libpq/fe-secure-gss.c
>> b/src/interfaces/libpq/fe-secure-gss.c
>> new file mode 100644
>> index 0000000..afea9c3
>> --- /dev/null
>> +++ b/src/interfaces/libpq/fe-secure-gss.c
>> @@ -0,0 +1,92 @@
>> +#include <assert.h>
>> You should add a proper header to those new files.
>
> Sorry, what?

All the files in the source tree need to have a header like that:
/*-------------------------------------------------------------------------
 *
 * file_name.c
 *      Description
 *
 * Portions Copyright (c) 2015, PostgreSQL Global Development Group
 *
 * IDENTIFICATION
 *       path/to/file/file_name.c
 *
 *-------------------------------------------------------------------------
 */
--
Michael


--
Sent via pgsql-hackers mailing list ([hidden email])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH v3] GSSAPI encryption support

Robbie Harwood
In reply to this post by Robbie Harwood
Alright, here's v3.  As requested, it's one patch now.  Other things
addressed herein include:

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

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

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

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

Patch follows.  Thanks!

From 6710d5ad0226ea3a5ea8e35d6dc54b4500f1d3e0 Mon Sep 17 00:00:00 2001
From: "Robbie Harwood (frozencemetery)" <[hidden email]>
Date: Mon, 8 Jun 2015 19:27:45 -0400
Subject: [PATCH] GSSAPI encryption support

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

diff --git a/configure b/configure
index b771a83..a542577 100755
--- a/configure
+++ b/configure
@@ -712,6 +712,7 @@ with_uuid
 with_selinux
 with_openssl
 krb_srvtab
+with_gssapi
 with_python
 with_perl
 with_tcl
@@ -5488,6 +5489,7 @@ $as_echo "$with_gssapi" >&6; }
 
 
 
+
 #
 # Kerberos configuration parameters
 #
diff --git a/configure.in b/configure.in
index b5868b0..fccf542 100644
--- a/configure.in
+++ b/configure.in
@@ -636,6 +636,7 @@ PGAC_ARG_BOOL(with, gssapi, no, [build with GSSAPI support],
   krb_srvtab="FILE:\$(sysconfdir)/krb5.keytab"
 ])
 AC_MSG_RESULT([$with_gssapi])
+AC_SUBST(with_gssapi)
 
 
 AC_SUBST(krb_srvtab)
diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml
index 3b2935c..e6456a1 100644
--- a/doc/src/sgml/client-auth.sgml
+++ b/doc/src/sgml/client-auth.sgml
@@ -913,9 +913,10 @@ omicron         bryanh                  guest1
     <productname>GSSAPI</productname> with <productname>Kerberos</productname>
     authentication according to RFC 1964. <productname>GSSAPI</productname>
     provides automatic authentication (single sign-on) for systems
-    that support it. The authentication itself is secure, but the
-    data sent over the database connection will be sent unencrypted unless
-    <acronym>SSL</acronym> is used.
+    that support it. The authentication itself is secure, and GSSAPI can be
+    used for connection encryption as well (see the
+    <literal>require_encrypt</literal> parameter below); <acronym>SSL</acronym>
+    can also be used for connection security.
    </para>
 
    <para>
@@ -1046,6 +1047,18 @@ omicron         bryanh                  guest1
        </para>
       </listitem>
      </varlistentry>
+
+     <varlistentry>
+      <term><literal>require_encrypt</literal></term>
+      <listitem>
+       <para>
+        Whether to require GSSAPI encryption.  Default is off, which causes
+        GSSAPI encryption to be enabled if available and requested for
+        compatibility with old clients.  It is recommended to set this unless
+        old clients are present.
+       </para>
+      </listitem>
+     </varlistentry>
     </variablelist>
    </para>
   </sect2>
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 0ee018e..32b4f1e 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1356,6 +1356,18 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
       </listitem>
      </varlistentry>
 
+     <varlistentry id="libpq-connect-gss-enc-require" xreflabel="gss-enc-require">
+      <term><literal>gss_enc_require</literal></term>
+      <listitem>
+       <para>
+        If set, whether to require GSSAPI encryption support from the remote
+        server. Defaults to unset, which will cause the client to fall back to
+        not using GSSAPI encryption if the server does not support encryption
+        through GSSAPI.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="libpq-connect-service" xreflabel="service">
       <term><literal>service</literal></term>
       <listitem>
diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml
index 34da859..cc65ee6 100644
--- a/doc/src/sgml/protocol.sgml
+++ b/doc/src/sgml/protocol.sgml
@@ -1295,6 +1295,42 @@
     of authentication checking.
    </para>
   </sect2>
+
+  <sect2>
+   <title><acronym>GSSAPI</acronym> Session Encryption</title>
+
+   <para>
+    If <productname>PostgreSQL</> was built with
+    <acronym>GSSAPI</acronym> and <acronym>GSSAPI</acronym> support, traffic
+    can also be encrypted using <acronym>GSSAPI</acronym>. To force encryption
+    using <acronym>GSSAPI</acronym>, set require_encrypt in
+    <filename>pg_hba.conf</filename>.
+   </para>
+
+   <para>
+    In order to probe for <acronym>GSSAPI</acronym> support, the client will
+    include in their StartupMessage the parameter gss_encrypt. If the server
+    does not support <acronym>GSSAPI</acronym> or <acronym>GSSAPI</acronym>
+    encryption, the server will error the connection; otherwise, it continues
+    as normal. The client may retry the connection
+    without <acronym>GSSAPI</acronym> encryption support depending on its
+    settings. If the client does not probe support, depending on settings
+    in <filename>pg_hba.conf</filename>, the server may drop
+    <acronym>GSSAPI</acronym>-authenticated connections without encryption.
+   </para>
+
+   <para>
+    If the client has probed <acronym>GSSAPI</acronym> encryption support and
+    the connection is <acronym>GSSAPI</acronym>-authenticated, then after the
+    server sends AuthenticationOk, all traffic between the client and server
+    will be <acronym>GSSAPI</acronym>-encrypted.  See message formats above.
+   </para>
+
+   <para>
+     It is valid to use <acronym>GSSAPI</acronym> encryption over
+     <acronym>SSL</acronym>-encrypted connections.
+   </para>
+  </sect2>
  </sect1>
 
 <sect1 id="protocol-replication">
@@ -2202,7 +2238,9 @@ Notice that although each message includes a byte count at the beginning,
 the message format is defined so that the message end can be found without
 reference to the byte count.  This aids validity checking.  (The CopyData
 message is an exception, because it forms part of a data stream; the contents
-of any individual CopyData message cannot be interpretable on their own.)
+of any individual CopyData message cannot be interpretable on their own.  The
+GSSAPI message type is another exception because GSSAPI does not require
+payloads to include length information.)
 </para>
 
 <variablelist>
@@ -3932,6 +3970,48 @@ FunctionCallResponse (B)
 </listitem>
 </varlistentry>
 
+<varlistentry>
+<term>
+GSSAPI (F &amp; B)
+</term>
+<listitem>
+<para>
+<variablelist>
+<varlistentry>
+<term>
+        Byte1('g')
+</term>
+<listitem>
+<para>
+                Identifies the message as GSSAPI-encrypted data.
+</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>
+        Int32
+</term>
+<listitem>
+<para>
+                Length of message contents in bytes, including self.
+</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>
+        Byte<replaceable>n</replaceable>
+</term>
+<listitem>
+<para>
+                GSSAPI-encrypted data.  Once decrypted, will be in a valid
+                message format.
+</para>
+</listitem>
+</varlistentry>
+</variablelist>
+</para>
+</listitem>
+</varlistentry>
 
 <varlistentry>
 <term>
diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml
index 6d5b108..0d65fd6 100644
--- a/doc/src/sgml/runtime.sgml
+++ b/doc/src/sgml/runtime.sgml
@@ -1868,7 +1868,7 @@ pg_dumpall -p 5432 | psql -d postgres -p 5433
   </para>
 
   <para>
-   To prevent spoofing on TCP connections, the best solution is to use
+   To prevent spoofing on TCP connections, one possible solution is to use
    SSL certificates and make sure that clients check the server's certificate.
    To do that, the server
    must be configured to accept only <literal>hostssl</> connections (<xref
@@ -1878,6 +1878,15 @@ pg_dumpall -p 5432 | psql -d postgres -p 5433
    <literal>verify-full</> and have the appropriate root certificate
    file installed (<xref linkend="libq-ssl-certificates">).
   </para>
+
+  <para>
+   Another way of preventing spoofing on TCP connections is to use GSSAPI
+   encryption. In order to force all GSSAPI connections to be encrypted, one
+   should set <literal>require_encrypt</> in <filename>pg_hba.conf</> on GSS
+   connections. Then, using Kerberos, the client and server will mutually
+   authenticate, and the connection will be encrypted once the authentication
+   step is complete.
+  </para>
  </sect1>
 
  <sect1 id="encryption-options">
@@ -1993,6 +2002,15 @@ pg_dumpall -p 5432 | psql -d postgres -p 5433
       connect to servers only via SSL. <application>Stunnel</> or
       <application>SSH</> can also be used to encrypt transmissions.
      </para>
+
+     <para>
+      GSSAPI connections can also encrypt all data sent across the network. In
+      the <filename>pg_hba.conf</> file, the GSSAPI authentication method has
+      a parameter to require encryption; otherwise, connections will be
+      encrypted if available and requested by the client. On the client side,
+      there is also a parameter to require GSSAPI encryption support from the
+      server.
+     </para>
     </listitem>
   </varlistentry>
 
diff --git a/src/Makefile.global.in b/src/Makefile.global.in
index 51f4797..5814440 100644
--- a/src/Makefile.global.in
+++ b/src/Makefile.global.in
@@ -183,6 +183,7 @@ with_perl = @with_perl@
 with_python = @with_python@
 with_tcl = @with_tcl@
 with_openssl = @with_openssl@
+with_gssapi = @with_gssapi@
 with_selinux = @with_selinux@
 with_libxml = @with_libxml@
 with_libxslt = @with_libxslt@
diff --git a/src/backend/libpq/Makefile b/src/backend/libpq/Makefile
index 09410c4..ee69b82 100644
--- a/src/backend/libpq/Makefile
+++ b/src/backend/libpq/Makefile
@@ -21,4 +21,8 @@ ifeq ($(with_openssl),yes)
 OBJS += be-secure-openssl.o
 endif
 
+ifeq ($(with_gssapi),yes)
+OBJS += be-gss.o
+endif
+
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index aca4ffe..83ff56f 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -36,7 +36,6 @@
  * Global authentication functions
  *----------------------------------------------------------------
  */
-static void sendAuthRequest(Port *port, AuthRequest areq);
 static void auth_failed(Port *port, int status, char *logdetail);
 static char *recv_password_packet(Port *port);
 static int recv_and_check_password_packet(Port *port, char **logdetail);
@@ -122,30 +121,6 @@ static int CheckLDAPAuth(Port *port);
 static int CheckCertAuth(Port *port);
 #endif
 
-
-/*----------------------------------------------------------------
- * Kerberos and GSSAPI GUCs
- *----------------------------------------------------------------
- */
-char   *pg_krb_server_keyfile;
-bool pg_krb_caseins_users;
-
-
-/*----------------------------------------------------------------
- * GSSAPI Authentication
- *----------------------------------------------------------------
- */
-#ifdef ENABLE_GSS
-#if defined(HAVE_GSSAPI_H)
-#include <gssapi.h>
-#else
-#include <gssapi/gssapi.h>
-#endif
-
-static int pg_GSS_recvauth(Port *port);
-#endif   /* ENABLE_GSS */
-
-
 /*----------------------------------------------------------------
  * SSPI Authentication
  *----------------------------------------------------------------
@@ -166,23 +141,6 @@ static int pg_SSPI_recvauth(Port *port);
 #endif
 static int CheckRADIUSAuth(Port *port);
 
-
-/*
- * Maximum accepted size of GSS and SSPI authentication tokens.
- *
- * Kerberos tickets are usually quite small, but the TGTs issued by Windows
- * domain controllers include an authorization field known as the Privilege
- * Attribute Certificate (PAC), which contains the user's Windows permissions
- * (group memberships etc.). The PAC is copied into all tickets obtained on
- * the basis of this TGT (even those issued by Unix realms which the Windows
- * realm trusts), and can be several kB in size. The maximum token size
- * accepted by Windows systems is determined by the MaxAuthToken Windows
- * registry setting. Microsoft recommends that it is not set higher than
- * 65535 bytes, so that seems like a reasonable limit for us as well.
- */
-#define PG_MAX_AUTH_TOKEN_LENGTH 65535
-
-
 /*----------------------------------------------------------------
  * Global authentication functions
  *----------------------------------------------------------------
@@ -565,7 +523,7 @@ ClientAuthentication(Port *port)
 /*
  * Send an authentication request packet to the frontend.
  */
-static void
+void
 sendAuthRequest(Port *port, AuthRequest areq)
 {
  StringInfoData buf;
@@ -708,300 +666,6 @@ recv_and_check_password_packet(Port *port, char **logdetail)
  return result;
 }
 
-
-
-/*----------------------------------------------------------------
- * GSSAPI authentication system
- *----------------------------------------------------------------
- */
-#ifdef ENABLE_GSS
-
-#if defined(WIN32) && !defined(WIN32_ONLY_COMPILER)
-/*
- * MIT Kerberos GSSAPI DLL doesn't properly export the symbols for MingW
- * that contain the OIDs required. Redefine here, values copied
- * from src/athena/auth/krb5/src/lib/gssapi/generic/gssapi_generic.c
- */
-static const gss_OID_desc GSS_C_NT_USER_NAME_desc =
-{10, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x02"};
-static GSS_DLLIMP gss_OID GSS_C_NT_USER_NAME = &GSS_C_NT_USER_NAME_desc;
-#endif
-
-
-static void
-pg_GSS_error(int severity, char *errmsg, OM_uint32 maj_stat, OM_uint32 min_stat)
-{
- gss_buffer_desc gmsg;
- OM_uint32 lmin_s,
- msg_ctx;
- char msg_major[128],
- msg_minor[128];
-
- /* Fetch major status message */
- msg_ctx = 0;
- gss_display_status(&lmin_s, maj_stat, GSS_C_GSS_CODE,
-   GSS_C_NO_OID, &msg_ctx, &gmsg);
- strlcpy(msg_major, gmsg.value, sizeof(msg_major));
- gss_release_buffer(&lmin_s, &gmsg);
-
- if (msg_ctx)
-
- /*
- * More than one message available. XXX: Should we loop and read all
- * messages? (same below)
- */
- ereport(WARNING,
- (errmsg_internal("incomplete GSS error report")));
-
- /* Fetch mechanism minor status message */
- msg_ctx = 0;
- gss_display_status(&lmin_s, min_stat, GSS_C_MECH_CODE,
-   GSS_C_NO_OID, &msg_ctx, &gmsg);
- strlcpy(msg_minor, gmsg.value, sizeof(msg_minor));
- gss_release_buffer(&lmin_s, &gmsg);
-
- if (msg_ctx)
- ereport(WARNING,
- (errmsg_internal("incomplete GSS minor error report")));
-
- /*
- * errmsg_internal, since translation of the first part must be done
- * before calling this function anyway.
- */
- ereport(severity,
- (errmsg_internal("%s", errmsg),
- errdetail_internal("%s: %s", msg_major, msg_minor)));
-}
-
-static int
-pg_GSS_recvauth(Port *port)
-{
- OM_uint32 maj_stat,
- min_stat,
- lmin_s,
- gflags;
- int mtype;
- int ret;
- StringInfoData buf;
- gss_buffer_desc gbuf;
-
- /*
- * GSS auth is not supported for protocol versions before 3, because it
- * relies on the overall message length word to determine the GSS payload
- * size in AuthenticationGSSContinue and PasswordMessage messages. (This
- * is, in fact, a design error in our GSS support, because protocol
- * messages are supposed to be parsable without relying on the length
- * word; but it's not worth changing it now.)
- */
- if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
- ereport(FATAL,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("GSSAPI is not supported in protocol version 2")));
-
- if (pg_krb_server_keyfile && strlen(pg_krb_server_keyfile) > 0)
- {
- /*
- * Set default Kerberos keytab file for the Krb5 mechanism.
- *
- * setenv("KRB5_KTNAME", pg_krb_server_keyfile, 0); except setenv()
- * not always available.
- */
- if (getenv("KRB5_KTNAME") == NULL)
- {
- size_t kt_len = strlen(pg_krb_server_keyfile) + 14;
- char   *kt_path = malloc(kt_len);
-
- if (!kt_path ||
- snprintf(kt_path, kt_len, "KRB5_KTNAME=%s",
- pg_krb_server_keyfile) != kt_len - 2 ||
- putenv(kt_path) != 0)
- {
- ereport(LOG,
- (errcode(ERRCODE_OUT_OF_MEMORY),
- errmsg("out of memory")));
- return STATUS_ERROR;
- }
- }
- }
-
- /*
- * We accept any service principal that's present in our keytab. This
- * increases interoperability between kerberos implementations that see
- * for example case sensitivity differently, while not really opening up
- * any vector of attack.
- */
- port->gss->cred = GSS_C_NO_CREDENTIAL;
-
- /*
- * Initialize sequence with an empty context
- */
- port->gss->ctx = GSS_C_NO_CONTEXT;
-
- /*
- * Loop through GSSAPI message exchange. This exchange can consist of
- * multiple messags sent in both directions. First message is always from
- * the client. All messages from client to server are password packets
- * (type 'p').
- */
- do
- {
- pq_startmsgread();
-
- CHECK_FOR_INTERRUPTS();
-
- mtype = pq_getbyte();
- if (mtype != 'p')
- {
- /* Only log error if client didn't disconnect. */
- if (mtype != EOF)
- ereport(COMMERROR,
- (errcode(ERRCODE_PROTOCOL_VIOLATION),
- errmsg("expected GSS response, got message type %d",
- mtype)));
- return STATUS_ERROR;
- }
-
- /* Get the actual GSS token */
- initStringInfo(&buf);
- if (pq_getmessage(&buf, PG_MAX_AUTH_TOKEN_LENGTH))
- {
- /* EOF - pq_getmessage already logged error */
- pfree(buf.data);
- return STATUS_ERROR;
- }
-
- /* Map to GSSAPI style buffer */
- gbuf.length = buf.len;
- gbuf.value = buf.data;
-
- elog(DEBUG4, "Processing received GSS token of length %u",
- (unsigned int) gbuf.length);
-
- maj_stat = gss_accept_sec_context(
-  &min_stat,
-  &port->gss->ctx,
-  port->gss->cred,
-  &gbuf,
-  GSS_C_NO_CHANNEL_BINDINGS,
-  &port->gss->name,
-  NULL,
-  &port->gss->outbuf,
-  &gflags,
-  NULL,
-  NULL);
-
- /* gbuf no longer used */
- pfree(buf.data);
-
- elog(DEBUG5, "gss_accept_sec_context major: %d, "
- "minor: %d, outlen: %u, outflags: %x",
- maj_stat, min_stat,
- (unsigned int) port->gss->outbuf.length, gflags);
-
- CHECK_FOR_INTERRUPTS();
-
- if (port->gss->outbuf.length != 0)
- {
- /*
- * Negotiation generated data to be sent to the client.
- */
- elog(DEBUG4, "sending GSS response token of length %u",
- (unsigned int) port->gss->outbuf.length);
-
- sendAuthRequest(port, AUTH_REQ_GSS_CONT);
-
- gss_release_buffer(&lmin_s, &port->gss->outbuf);
- }
-
- if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
- {
- gss_delete_sec_context(&lmin_s, &port->gss->ctx, GSS_C_NO_BUFFER);
- pg_GSS_error(ERROR,
-   gettext_noop("accepting GSS security context failed"),
- maj_stat, min_stat);
- }
-
- if (maj_stat == GSS_S_CONTINUE_NEEDED)
- elog(DEBUG4, "GSS continue needed");
-
- } while (maj_stat == GSS_S_CONTINUE_NEEDED);
-
- if (port->gss->cred != GSS_C_NO_CREDENTIAL)
- {
- /*
- * Release service principal credentials
- */
- gss_release_cred(&min_stat, &port->gss->cred);
- }
-
- /*
- * GSS_S_COMPLETE indicates that authentication is now complete.
- *
- * Get the name of the user that authenticated, and compare it to the pg
- * username that was specified for the connection.
- */
- maj_stat = gss_display_name(&min_stat, port->gss->name, &gbuf, NULL);
- if (maj_stat != GSS_S_COMPLETE)
- pg_GSS_error(ERROR,
- gettext_noop("retrieving GSS user name failed"),
- maj_stat, min_stat);
-
- /*
- * Split the username at the realm separator
- */
- if (strchr(gbuf.value, '@'))
- {
- char   *cp = strchr(gbuf.value, '@');
-
- /*
- * If we are not going to include the realm in the username that is
- * passed to the ident map, destructively modify it here to remove the
- * realm. Then advance past the separator to check the realm.
- */
- if (!port->hba->include_realm)
- *cp = '\0';
- cp++;
-
- if (port->hba->krb_realm != NULL && strlen(port->hba->krb_realm))
- {
- /*
- * Match the realm part of the name first
- */
- if (pg_krb_caseins_users)
- ret = pg_strcasecmp(port->hba->krb_realm, cp);
- else
- ret = strcmp(port->hba->krb_realm, cp);
-
- if (ret)
- {
- /* GSS realm does not match */
- elog(DEBUG2,
-   "GSSAPI realm (%s) and configured realm (%s) don't match",
- cp, port->hba->krb_realm);
- gss_release_buffer(&lmin_s, &gbuf);
- return STATUS_ERROR;
- }
- }
- }
- else if (port->hba->krb_realm && strlen(port->hba->krb_realm))
- {
- elog(DEBUG2,
- "GSSAPI did not return realm but realm matching was requested");
-
- gss_release_buffer(&lmin_s, &gbuf);
- return STATUS_ERROR;
- }
-
- ret = check_usermap(port->hba->usermap, port->user_name, gbuf.value,
- pg_krb_caseins_users);
-
- gss_release_buffer(&lmin_s, &gbuf);
-
- return ret;
-}
-#endif   /* ENABLE_GSS */
-
-
 /*----------------------------------------------------------------
  * SSPI authentication system
  *----------------------------------------------------------------
diff --git a/src/backend/libpq/be-gss.c b/src/backend/libpq/be-gss.c
new file mode 100644
index 0000000..2c3e58e
--- /dev/null
+++ b/src/backend/libpq/be-gss.c
@@ -0,0 +1,397 @@
+/*-------------------------------------------------------------------------
+ *
+ * be-gss.c
+ *  functions for GSSAPI support in the backend.
+ *
+ * Portions Copyright (c) 2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *  src/backend/libpq/be-gss.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "libpq/libpq.h"
+#include "libpq/auth.h"
+#include "miscadmin.h"
+
+#include <assert.h>
+
+#if defined(WIN32) && !defined(WIN32_ONLY_COMPILER)
+/*
+ * MIT Kerberos GSSAPI DLL doesn't properly export the symbols for MingW
+ * that contain the OIDs required. Redefine here, values copied
+ * from src/athena/auth/krb5/src/lib/gssapi/generic/gssapi_generic.c
+ */
+static const gss_OID_desc GSS_C_NT_USER_NAME_desc =
+{10, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x02"};
+static GSS_DLLIMP gss_OID GSS_C_NT_USER_NAME = &GSS_C_NT_USER_NAME_desc;
+#endif
+
+void
+pg_GSS_error(int severity, char *errmsg, OM_uint32 maj_stat, OM_uint32 min_stat)
+{
+ gss_buffer_desc gmsg;
+ OM_uint32 lmin_s,
+ msg_ctx;
+ char msg_major[128],
+ msg_minor[128];
+
+ /* Fetch major status message */
+ msg_ctx = 0;
+ gss_display_status(&lmin_s, maj_stat, GSS_C_GSS_CODE,
+   GSS_C_NO_OID, &msg_ctx, &gmsg);
+ strlcpy(msg_major, gmsg.value, sizeof(msg_major));
+ gss_release_buffer(&lmin_s, &gmsg);
+
+ if (msg_ctx)
+
+ /*
+ * More than one message available. XXX: Should we loop and read all
+ * messages? (same below)
+ */
+ ereport(WARNING,
+ (errmsg_internal("incomplete GSS error report")));
+
+ /* Fetch mechanism minor status message */
+ msg_ctx = 0;
+ gss_display_status(&lmin_s, min_stat, GSS_C_MECH_CODE,
+   GSS_C_NO_OID, &msg_ctx, &gmsg);
+ strlcpy(msg_minor, gmsg.value, sizeof(msg_minor));
+ gss_release_buffer(&lmin_s, &gmsg);
+
+ if (msg_ctx)
+ ereport(WARNING,
+ (errmsg_internal("incomplete GSS minor error report")));
+
+ /*
+ * errmsg_internal, since translation of the first part must be done
+ * before calling this function anyway.
+ */
+ ereport(severity,
+ (errmsg_internal("%s", errmsg),
+ errdetail_internal("%s: %s", msg_major, msg_minor)));
+}
+
+int
+pg_GSS_recvauth(Port *port)
+{
+ OM_uint32 maj_stat,
+ min_stat,
+ lmin_s,
+ gflags;
+ int mtype;
+ int ret;
+ StringInfoData buf;
+ gss_buffer_desc gbuf;
+
+ /*
+ * GSS auth is not supported for protocol versions before 3, because it
+ * relies on the overall message length word to determine the GSS payload
+ * size in AuthenticationGSSContinue and PasswordMessage messages. (This
+ * is, in fact, a design error in our GSS support, because protocol
+ * messages are supposed to be parsable without relying on the length
+ * word; but it's not worth changing it now.)
+ */
+ if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
+ ereport(FATAL,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("GSSAPI is not supported in protocol version 2")));
+
+ if (pg_krb_server_keyfile && strlen(pg_krb_server_keyfile) > 0)
+ {
+ /*
+ * Set default Kerberos keytab file for the Krb5 mechanism.
+ *
+ * setenv("KRB5_KTNAME", pg_krb_server_keyfile, 0); except setenv()
+ * not always available.
+ */
+ if (getenv("KRB5_KTNAME") == NULL)
+ {
+ size_t kt_len = strlen(pg_krb_server_keyfile) + 14;
+ char   *kt_path = malloc(kt_len);
+
+ if (!kt_path ||
+ snprintf(kt_path, kt_len, "KRB5_KTNAME=%s",
+ pg_krb_server_keyfile) != kt_len - 2 ||
+ putenv(kt_path) != 0)
+ {
+ ereport(LOG,
+ (errcode(ERRCODE_OUT_OF_MEMORY),
+ errmsg("out of memory")));
+ return STATUS_ERROR;
+ }
+ }
+ }
+
+ /*
+ * We accept any service principal that's present in our keytab. This
+ * increases interoperability between kerberos implementations that see
+ * for example case sensitivity differently, while not really opening up
+ * any vector of attack.
+ */
+ port->gss->cred = GSS_C_NO_CREDENTIAL;
+
+ /*
+ * Initialize sequence with an empty context
+ */
+ port->gss->ctx = GSS_C_NO_CONTEXT;
+
+ /*
+ * Loop through GSSAPI message exchange. This exchange can consist of
+ * multiple messags sent in both directions. First message is always from
+ * the client. All messages from client to server are password packets
+ * (type 'p').
+ */
+ do
+ {
+ pq_startmsgread();
+
+ CHECK_FOR_INTERRUPTS();
+
+ mtype = pq_getbyte();
+ if (mtype != 'p')
+ {
+ /* Only log error if client didn't disconnect. */
+ if (mtype != EOF)
+ ereport(COMMERROR,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("expected GSS response, got message type %d",
+ mtype)));
+ return STATUS_ERROR;
+ }
+
+ /* Get the actual GSS token */
+ initStringInfo(&buf);
+ if (pq_getmessage(&buf, PG_MAX_AUTH_TOKEN_LENGTH))
+ {
+ /* EOF - pq_getmessage already logged error */
+ pfree(buf.data);
+ return STATUS_ERROR;
+ }
+
+ /* Map to GSSAPI style buffer */
+ gbuf.length = buf.len;
+ gbuf.value = buf.data;
+
+ elog(DEBUG4, "Processing received GSS token of length %u",
+ (unsigned int) gbuf.length);
+
+ maj_stat = gss_accept_sec_context(
+  &min_stat,
+  &port->gss->ctx,
+  port->gss->cred,
+  &gbuf,
+  GSS_C_NO_CHANNEL_BINDINGS,
+  &port->gss->name,
+  NULL,
+  &port->gss->outbuf,
+  &gflags,
+  NULL,
+  NULL);
+
+ /* gbuf no longer used */
+ pfree(buf.data);
+
+ elog(DEBUG5, "gss_accept_sec_context major: %d, "
+ "minor: %d, outlen: %u, outflags: %x",
+ maj_stat, min_stat,
+ (unsigned int) port->gss->outbuf.length, gflags);
+
+ CHECK_FOR_INTERRUPTS();
+
+ if (port->gss->outbuf.length != 0)
+ {
+ /*
+ * Negotiation generated data to be sent to the client.
+ */
+ elog(DEBUG4, "sending GSS response token of length %u",
+ (unsigned int) port->gss->outbuf.length);
+
+ sendAuthRequest(port, AUTH_REQ_GSS_CONT);
+
+ gss_release_buffer(&lmin_s, &port->gss->outbuf);
+ }
+
+ if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
+ {
+ gss_delete_sec_context(&lmin_s, &port->gss->ctx, GSS_C_NO_BUFFER);
+ pg_GSS_error(ERROR,
+   gettext_noop("accepting GSS security context failed"),
+ maj_stat, min_stat);
+ }
+
+ if (maj_stat == GSS_S_CONTINUE_NEEDED)
+ elog(DEBUG4, "GSS continue needed");
+
+ } while (maj_stat == GSS_S_CONTINUE_NEEDED);
+
+ if (port->gss->cred != GSS_C_NO_CREDENTIAL)
+ {
+ /*
+ * Release service principal credentials
+ */
+ gss_release_cred(&min_stat, &port->gss->cred);
+ }
+
+ /*
+ * GSS_S_COMPLETE indicates that authentication is now complete.
+ *
+ * Get the name of the user that authenticated, and compare it to the pg
+ * username that was specified for the connection.
+ */
+ maj_stat = gss_display_name(&min_stat, port->gss->name, &gbuf, NULL);
+ if (maj_stat != GSS_S_COMPLETE)
+ pg_GSS_error(ERROR,
+ gettext_noop("retrieving GSS user name failed"),
+ maj_stat, min_stat);
+
+ /*
+ * Split the username at the realm separator
+ */
+ if (strchr(gbuf.value, '@'))
+ {
+ char   *cp = strchr(gbuf.value, '@');
+
+ /*
+ * If we are not going to include the realm in the username that is
+ * passed to the ident map, destructively modify it here to remove the
+ * realm. Then advance past the separator to check the realm.
+ */
+ if (!port->hba->include_realm)
+ *cp = '\0';
+ cp++;
+
+ if (port->hba->krb_realm != NULL && strlen(port->hba->krb_realm))
+ {
+ /*
+ * Match the realm part of the name first
+ */
+ if (pg_krb_caseins_users)
+ ret = pg_strcasecmp(port->hba->krb_realm, cp);
+ else
+ ret = strcmp(port->hba->krb_realm, cp);
+
+ if (ret)
+ {
+ /* GSS realm does not match */
+ elog(DEBUG2,
+   "GSSAPI realm (%s) and configured realm (%s) don't match",
+ cp, port->hba->krb_realm);
+ gss_release_buffer(&lmin_s, &gbuf);
+ return STATUS_ERROR;
+ }
+ }
+ }
+ else if (port->hba->krb_realm && strlen(port->hba->krb_realm))
+ {
+ elog(DEBUG2,
+ "GSSAPI did not return realm but realm matching was requested");
+
+ gss_release_buffer(&lmin_s, &gbuf);
+ return STATUS_ERROR;
+ }
+
+ ret = check_usermap(port->hba->usermap, port->user_name, gbuf.value,
+ pg_krb_caseins_users);
+
+ gss_release_buffer(&lmin_s, &gbuf);
+
+ return ret;
+}
+
+size_t
+be_gss_encrypt(Port *port, char msgtype, const char **msgptr, size_t len)
+{
+ OM_uint32 major, minor;
+ gss_buffer_desc input, output;
+ uint32 len_n;
+ int conf;
+ char *ptr = *((char **) msgptr);
+ char *newbuf = palloc(len + 5);
+
+ len += 4;
+ len_n = htonl(len);
+
+ newbuf[0] = msgtype;
+ memcpy(newbuf + 1, &len_n, 4);
+ memcpy(newbuf + 5, ptr, len - 4);
+
+ input.length = len + 1; /* include type */
+ input.value = newbuf;
+ output.length = 0;
+ output.value = NULL;
+
+ major = gss_wrap(&minor, port->gss->ctx, 1, GSS_C_QOP_DEFAULT, &input,
+ &conf, &output);
+ if (GSS_ERROR(major))
+ {
+ pg_GSS_error(ERROR, gettext_noop("unwrapping GSS message failed"),
+ major, minor);
+ return -1;
+ }
+ assert(conf);
+
+ newbuf = repalloc(newbuf, output.length);
+ memcpy(newbuf, output.value, output.length);
+
+ len = output.length;
+ *msgptr = newbuf;
+ gss_release_buffer(&minor, &output);
+
+ return len;
+}
+
+int
+be_gss_inplace_decrypt(StringInfo inBuf)
+{
+ OM_uint32 major, minor;
+ gss_buffer_desc input, output;
+ int qtype, conf;
+ size_t msglen = 0;
+
+ input.length = inBuf->len;
+ input.value = inBuf->data;
+ output.length = 0;
+ output.value = NULL;
+
+ major = gss_unwrap(&minor, MyProcPort->gss->ctx, &input, &output,
+   &conf, NULL);
+ if (GSS_ERROR(major))
+ {
+ pg_GSS_error(ERROR, gettext_noop("wrapping GSS message failed"),
+ major, minor);
+ return -1;
+ }
+ else if (conf == 0)
+ {
+ ereport(COMMERROR,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("Expected GSSAPI confidentiality but it was not received")));
+ gss_release_buffer(&minor, &output);
+ return -1;
+ }
+
+ qtype = ((char *)output.value)[0]; /* first byte is message type */
+ inBuf->len = output.length - 5; /* message starts */
+
+ memcpy((char *)&msglen, ((char *)output.value) + 1, 4);
+ msglen = ntohl(msglen);
+ if (msglen - 4 != inBuf->len)
+ {
+ ereport(COMMERROR,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("Length value inside GSSAPI-encrypted packet was malformed")));
+ gss_release_buffer(&minor, &output);
+ return -1;
+ }
+
+ memcpy(inBuf->data, ((char *)output.value) + 5, inBuf->len);
+ inBuf->data[inBuf->len] = '\0'; /* invariant */
+ gss_release_buffer(&minor, &output);
+
+ return qtype;
+}
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 23c8b5d..90fe57f 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -1570,6 +1570,15 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, int line_num)
  else
  hbaline->include_realm = false;
  }
+ else if (strcmp(name, "require_encrypt") == 0)
+ {
+ if (hbaline->auth_method != uaGSS)
+ INVALID_AUTH_OPTION("require_encrypt", "gssapi");
+ if (strcmp(val, "1") == 0)
+ hbaline->require_encrypt = true;
+ else
+ hbaline->require_encrypt = false;
+ }
  else if (strcmp(name, "radiusserver") == 0)
  {
  struct addrinfo *gai_result;
diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c
index 63673b1..2603951 100644
--- a/src/backend/libpq/pqcomm.c
+++ b/src/backend/libpq/pqcomm.c
@@ -1513,6 +1513,19 @@ socket_putmessage(char msgtype, const char *s, size_t len)
 {
  if (DoingCopyOut || PqCommBusy)
  return 0;
+
+#ifdef ENABLE_GSS
+ /* Do not wrap auth requests. */
+ if (MyProcPort->hba->auth_method == uaGSS && gss_encrypt &&
+ msgtype != 'R' && msgtype != 'g')
+ {
+ len = be_gss_encrypt(MyProcPort, msgtype, &s, len);
+ if (len < 0)
+ goto fail;
+ msgtype = 'g';
+ }
+#endif
+
  PqCommBusy = true;
  if (msgtype)
  if (internal_putbytes(&msgtype, 1))
@@ -1528,10 +1541,20 @@ socket_putmessage(char msgtype, const char *s, size_t len)
  if (internal_putbytes(s, len))
  goto fail;
  PqCommBusy = false;
+#ifdef ENABLE_GSS
+ /* if we're GSSAPI encrypting, s was allocated in be_gss_encrypt */
+ if (msgtype == 'g')
+ pfree((char *)s);
+#endif
  return 0;
 
 fail:
  PqCommBusy = false;
+#ifdef ENABLE_GSS
+ /* if we're GSSAPI encrypting, s was allocated in be_gss_encrypt */
+ if (msgtype == 'g')
+ pfree((char *)s);
+#endif
  return EOF;
 }
 
@@ -1547,6 +1570,22 @@ socket_putmessage_noblock(char msgtype, const char *s, size_t len)
  int res PG_USED_FOR_ASSERTS_ONLY;
  int required;
 
+#ifdef ENABLE_GSS
+ /*
+ * Because socket_putmessage is also a front-facing function, we need the
+ * ability to GSSAPI encrypt from either.  Since socket_putmessage_noblock
+ * calls into socket_putmessage, socket_putmessage will handle freeing the
+ * allocated string.
+ */
+ if (gss_encrypt && msgtype != 'R' && msgtype != 'g')
+ {
+ len = be_gss_encrypt(MyProcPort, msgtype, &s, len);
+ if (len < 0)
+ return;
+ msgtype = 'g';
+ }
+#endif
+
  /*
  * Ensure we have enough space in the output buffer for the message header
  * as well as the message itself.
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index d30fe35..0dc0376 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -336,6 +336,7 @@ static int
 SocketBackend(StringInfo inBuf)
 {
  int qtype;
+ bool msg_got = false;
 
  /*
  * Get message type code from the frontend.
@@ -365,6 +366,33 @@ SocketBackend(StringInfo inBuf)
  return qtype;
  }
 
+#ifdef ENABLE_GSS
+ else if (qtype == 'g' && gss_encrypt &&
+ MyProcPort->hba->auth_method == uaGSS)
+ {
+ /* GSSAPI wrapping implies protocol >= 3 */
+ if (pq_getmessage(inBuf, 0))
+ return EOF;
+ msg_got = true;
+
+ qtype = be_gss_inplace_decrypt(inBuf);
+ if (qtype < 0)
+ return EOF;
+ }
+ else if (gss_encrypt && MyProcPort->hba->auth_method == uaGSS &&
+ qtype != 'g' && qtype != 'R' )
+ {
+ /*
+ * Either something malicious is occuring, or we have lost
+ * synchronization.
+ */
+ ereport(FATAL,
+ (errcode(ERRCODE_PROTOCOL_VIOLATION),
+ errmsg("invalid frontend message type %d", qtype)));
+ return EOF;
+ }
+#endif
+
  /*
  * Validate message type code before trying to read body; if we have lost
  * sync, better to say "command unknown" than to run out of memory because
@@ -490,7 +518,7 @@ SocketBackend(StringInfo inBuf)
  * after the type code; we can read the message contents independently of
  * the type.
  */
- if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
+ if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3 && !msg_got)
  {
  if (pq_getmessage(inBuf, 0))
  return EOF; /* suitable message already logged */
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 7b19714..201c831 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -32,7 +32,7 @@
 #include "catalog/pg_db_role_setting.h"
 #include "catalog/pg_tablespace.h"
 #include "libpq/auth.h"
-#include "libpq/libpq-be.h"
+#include "libpq/libpq.h"
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
 #include "pgstat.h"
@@ -1087,6 +1087,11 @@ process_startup_options(Port *port, bool am_superuser)
 
  SetConfigOption(name, value, gucctx, PGC_S_CLIENT);
  }
+
+ if (!gss_encrypt && port->hba->require_encrypt)
+ ereport(FATAL, (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
+ errmsg("GSSAPI encryption required from user \"%s\"",
+   port->user_name)));
 }
 
 /*
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 71090f2..b422fd1 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -186,6 +186,8 @@ static const char *show_log_file_mode(void);
 static ConfigVariable *ProcessConfigFileInternal(GucContext context,
   bool applySettings, int elevel);
 
+static void assign_gss_encrypt(bool newval, void *extra);
+static bool check_gss_encrypt(bool *newval, void **extra, GucSource source);
 
 /*
  * Options for enum values defined in this module.
@@ -477,6 +479,10 @@ static bool assert_enabled;
 /* should be static, but commands/variable.c needs to get at this */
 char   *role_string;
 
+/* Kerberos and GSSAPI GUCs */
+char   *pg_krb_server_keyfile;
+bool pg_krb_caseins_users;
+bool gss_encrypt;
 
 /*
  * Displayable names for context types (enum GucContext)
@@ -1610,6 +1616,15 @@ static struct config_bool ConfigureNamesBool[] =
  NULL, NULL, NULL
  },
 
+ {
+ {"gss_encrypt", PGC_USERSET, CONN_AUTH_SECURITY,
+ gettext_noop("Whether client wants encryption for this connection."),
+ NULL,
+ GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
+ },
+ &gss_encrypt, false, check_gss_encrypt, assign_gss_encrypt, NULL
+ },
+
  /* End-of-list marker */
  {
  {NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL, NULL
@@ -10108,4 +10123,19 @@ show_log_file_mode(void)
  return buf;
 }
 
+static void
+assign_gss_encrypt(bool newval, void *extra)
+{
+ gss_encrypt = newval;
+}
+
+static bool
+check_gss_encrypt(bool *newval, void **extra, GucSource source)
+{
+ if (MyProcPort && MyProcPort->hba && MyProcPort->hba->require_encrypt &&
+ !*newval)
+ return false;
+ return true;
+}
+
 #include "guc-file.c"
diff --git a/src/include/libpq/auth.h b/src/include/libpq/auth.h
index 80f26a8..ba197c6 100644
--- a/src/include/libpq/auth.h
+++ b/src/include/libpq/auth.h
@@ -26,4 +26,6 @@ extern void ClientAuthentication(Port *port);
 typedef void (*ClientAuthentication_hook_type) (Port *, int);
 extern PGDLLIMPORT ClientAuthentication_hook_type ClientAuthentication_hook;
 
+extern void sendAuthRequest(Port *port, AuthRequest areq);
+
 #endif   /* AUTH_H */
diff --git a/src/include/libpq/hba.h b/src/include/libpq/hba.h
index 68a953a..3435674 100644
--- a/src/include/libpq/hba.h
+++ b/src/include/libpq/hba.h
@@ -77,6 +77,7 @@ typedef struct HbaLine
  bool clientcert;
  char   *krb_realm;
  bool include_realm;
+ bool require_encrypt;
  char   *radiusserver;
  char   *radiussecret;
  char   *radiusidentifier;
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index caaa8b5..98839a0 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -30,6 +30,7 @@
 #endif
 
 #ifdef ENABLE_GSS
+
 #if defined(HAVE_GSSAPI_H)
 #include <gssapi.h>
 #else
@@ -68,7 +69,22 @@ typedef struct
 #include "datatype/timestamp.h"
 #include "libpq/hba.h"
 #include "libpq/pqcomm.h"
+#include "lib/stringinfo.h"
 
+/*
+ * Maximum accepted size of GSS and SSPI authentication tokens.
+ *
+ * Kerberos tickets are usually quite small, but the TGTs issued by Windows
+ * domain controllers include an authorization field known as the Privilege
+ * Attribute Certificate (PAC), which contains the user's Windows permissions
+ * (group memberships etc.). The PAC is copied into all tickets obtained on
+ * the basis of this TGT (even those issued by Unix realms which the Windows
+ * realm trusts), and can be several kB in size. The maximum token size
+ * accepted by Windows systems is determined by the MaxAuthToken Windows
+ * registry setting. Microsoft recommends that it is not set higher than
+ * 65535 bytes, so that seems like a reasonable limit for us as well.
+ */
+#define PG_MAX_AUTH_TOKEN_LENGTH 65535
 
 typedef enum CAC_state
 {
@@ -214,6 +230,16 @@ extern void be_tls_get_cipher(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peerdn_name(Port *port, char *ptr, size_t len);
 #endif
 
+#ifdef ENABLE_GSS
+/* These functions are implemented in be-gss.c */
+extern int pg_GSS_recvauth(Port *port);
+extern size_t
+be_gss_encrypt(Port *port, char msgtype, const char **msgptr, size_t len);
+extern int be_gss_inplace_decrypt(StringInfo inBuf);
+extern void pg_GSS_error(int severity, char *errmsg, OM_uint32 maj_stat,
+ OM_uint32 min_stat);
+#endif
+
 extern ProtocolVersion FrontendProtocol;
 
 /* TCP keepalives configuration. These are no-ops on an AF_UNIX socket. */
diff --git a/src/include/libpq/libpq.h b/src/include/libpq/libpq.h
index efb2dac..3fd6e7d 100644
--- a/src/include/libpq/libpq.h
+++ b/src/include/libpq/libpq.h
@@ -100,4 +100,6 @@ extern char *SSLCipherSuites;
 extern char *SSLECDHCurve;
 extern bool SSLPreferServerCiphers;
 
+extern bool gss_encrypt;
+
 #endif   /* LIBPQ_H */
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index c2105f1..807db20 100644
--- a/src/interfaces/libpq/Makefile
+++ b/src/interfaces/libpq/Makefile
@@ -48,6 +48,10 @@ ifeq ($(with_openssl),yes)
 OBJS += fe-secure-openssl.o
 endif
 
+ifeq ($(with_gssapi),yes)
+OBJS += fe-gss.o
+endif
+
 ifeq ($(PORTNAME), cygwin)
 override shlib = cyg$(NAME)$(DLSUFFIX)
 endif
diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c
index 5891c75..c606c90 100644
--- a/src/interfaces/libpq/fe-auth.c
+++ b/src/interfaces/libpq/fe-auth.c
@@ -42,188 +42,6 @@
 #include "fe-auth.h"
 #include "libpq/md5.h"
 
-
-#ifdef ENABLE_GSS
-/*
- * GSSAPI authentication system.
- */
-
-#if defined(WIN32) && !defined(WIN32_ONLY_COMPILER)
-/*
- * MIT Kerberos GSSAPI DLL doesn't properly export the symbols for MingW
- * that contain the OIDs required. Redefine here, values copied
- * from src/athena/auth/krb5/src/lib/gssapi/generic/gssapi_generic.c
- */
-static const gss_OID_desc GSS_C_NT_HOSTBASED_SERVICE_desc =
-{10, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04"};
-static GSS_DLLIMP gss_OID GSS_C_NT_HOSTBASED_SERVICE = &GSS_C_NT_HOSTBASED_SERVICE_desc;
-#endif
-
-/*
- * Fetch all errors of a specific type and append to "str".
- */
-static void
-pg_GSS_error_int(PQExpBuffer str, const char *mprefix,
- OM_uint32 stat, int type)
-{
- OM_uint32 lmin_s;
- gss_buffer_desc lmsg;
- OM_uint32 msg_ctx = 0;
-
- do
- {
- gss_display_status(&lmin_s, stat, type,
-   GSS_C_NO_OID, &msg_ctx, &lmsg);
- appendPQExpBuffer(str, "%s: %s\n", mprefix, (char *) lmsg.value);
- gss_release_buffer(&lmin_s, &lmsg);
- } while (msg_ctx);
-}
-
-/*
- * GSSAPI errors contain two parts; put both into conn->errorMessage.
- */
-static void
-pg_GSS_error(const char *mprefix, PGconn *conn,
- OM_uint32 maj_stat, OM_uint32 min_stat)
-{
- resetPQExpBuffer(&conn->errorMessage);
-
- /* Fetch major error codes */
- pg_GSS_error_int(&conn->errorMessage, mprefix, maj_stat, GSS_C_GSS_CODE);
-
- /* Add the minor codes as well */
- pg_GSS_error_int(&conn->errorMessage, mprefix, min_stat, GSS_C_MECH_CODE);
-}
-
-/*
- * Continue GSS authentication with next token as needed.
- */
-static int
-pg_GSS_continue(PGconn *conn)
-{
- OM_uint32 maj_stat,
- min_stat,
- lmin_s;
-
- maj_stat = gss_init_sec_context(&min_stat,
- GSS_C_NO_CREDENTIAL,
- &conn->gctx,
- conn->gtarg_nam,
- GSS_C_NO_OID,
- GSS_C_MUTUAL_FLAG,
- 0,
- GSS_C_NO_CHANNEL_BINDINGS,
-  (conn->gctx == GSS_C_NO_CONTEXT) ? GSS_C_NO_BUFFER : &conn->ginbuf,
- NULL,
- &conn->goutbuf,
- NULL,
- NULL);
-
- if (conn->gctx != GSS_C_NO_CONTEXT)
- {
- free(conn->ginbuf.value);
- conn->ginbuf.value = NULL;
- conn->ginbuf.length = 0;
- }
-
- if (conn->goutbuf.length != 0)
- {
- /*
- * GSS generated data to send to the server. We don't care if it's the
- * first or subsequent packet, just send the same kind of password
- * packet.
- */
- if (pqPacketSend(conn, 'p',
- conn->goutbuf.value, conn->goutbuf.length)
- != STATUS_OK)
- {
- gss_release_buffer(&lmin_s, &conn->goutbuf);
- return STATUS_ERROR;
- }
- }
- gss_release_buffer(&lmin_s, &conn->goutbuf);
-
- if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
- {
- pg_GSS_error(libpq_gettext("GSSAPI continuation error"),
- conn,
- maj_stat, min_stat);
- gss_release_name(&lmin_s, &conn->gtarg_nam);
- if (conn->gctx)
- gss_delete_sec_context(&lmin_s, &conn->gctx, GSS_C_NO_BUFFER);
- return STATUS_ERROR;
- }
-
- if (maj_stat == GSS_S_COMPLETE)
- gss_release_name(&lmin_s, &conn->gtarg_nam);
-
- return STATUS_OK;
-}
-
-/*
- * Send initial GSS authentication token
- */
-static int
-pg_GSS_startup(PGconn *conn)
-{
- OM_uint32 maj_stat,
- min_stat;
- int maxlen;
- gss_buffer_desc temp_gbuf;
-
- if (!(conn->pghost && conn->pghost[0] != '\0'))
- {
- printfPQExpBuffer(&conn->errorMessage,
-  libpq_gettext("host name must be specified\n"));
- return STATUS_ERROR;
- }
-
- if (conn->gctx)
- {
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("duplicate GSS authentication request\n"));
- return STATUS_ERROR;
- }
-
- /*
- * Import service principal name so the proper ticket can be acquired by
- * the GSSAPI system.
- */
- maxlen = NI_MAXHOST + strlen(conn->krbsrvname) + 2;
- temp_gbuf.value = (char *) malloc(maxlen);
- if (!temp_gbuf.value)
- {
- printfPQExpBuffer(&conn->errorMessage,
-  libpq_gettext("out of memory\n"));
- return STATUS_ERROR;
- }
- snprintf(temp_gbuf.value, maxlen, "%s@%s",
- conn->krbsrvname, conn->pghost);
- temp_gbuf.length = strlen(temp_gbuf.value);
-
- maj_stat = gss_import_name(&min_stat, &temp_gbuf,
-   GSS_C_NT_HOSTBASED_SERVICE, &conn->gtarg_nam);
- free(temp_gbuf.value);
-
- if (maj_stat != GSS_S_COMPLETE)
- {
- pg_GSS_error(libpq_gettext("GSSAPI name import error"),
- conn,
- maj_stat, min_stat);
- return STATUS_ERROR;
- }
-
- /*
- * Initial packet is the same as a continuation packet with no initial
- * context.
- */
- conn->gctx = GSS_C_NO_CONTEXT;
-
- return pg_GSS_continue(conn);
-}
-#endif   /* ENABLE_GSS */
-
-
 #ifdef ENABLE_SSPI
 /*
  * SSPI authentication system (Windows only)
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index f3030fb..4fcffa3 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -91,8 +91,9 @@ static int ldapServiceLookup(const char *purl, PQconninfoOption *options,
  * application_name in a startup packet.  We hard-wire the value rather
  * than looking into errcodes.h since it reflects historical behavior
  * rather than that of the current code.
+ * Servers that do not support GSS encryption will also return this error.
  */
-#define ERRCODE_APPNAME_UNKNOWN "42704"
+#define ERRCODE_UNKNOWN_PARAM "42704"
 
 /* This is part of the protocol so just define it */
 #define ERRCODE_INVALID_PASSWORD "28P01"
@@ -296,6 +297,12 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
  offsetof(struct pg_conn, gsslib)},
 #endif
 
+#if defined(ENABLE_GSS)
+ {"gss_enc_require", "GSS_ENC_REQUIRE", "0", NULL,
+ "Require-GSS-encryption", "", 1, /* should be '0' or '1' */
+ offsetof(struct pg_conn, gss_enc_require)},
+#endif
+
  {"replication", NULL, NULL, NULL,
  "Replication", "D", 5,
  offsetof(struct pg_conn, replication)},
@@ -2512,6 +2519,11 @@ keep_going: /* We will come back to here until there is
  /* We are done with authentication exchange */
  conn->status = CONNECTION_AUTH_OK;
 
+#ifdef ENABLE_GSS
+ if (conn->gctx != 0)
+ conn->gss_auth_done = true;
+#endif
+
  /*
  * Set asyncStatus so that PQgetResult will think that
  * what comes back next is the result of a query.  See
@@ -2552,6 +2564,37 @@ keep_going: /* We will come back to here until there is
  if (res->resultStatus != PGRES_FATAL_ERROR)
  appendPQExpBufferStr(&conn->errorMessage,
  libpq_gettext("unexpected message from server during startup\n"));
+#ifdef ENABLE_GSS
+ else if (!conn->gss_disable_enc &&
+ *conn->gss_enc_require != '1')
+ {
+ /*
+ * We tried to request GSS encryption, but the server
+ * doesn't support it.  Retries are permitted here, so
+ * hang up and try again.  A connection that doesn't
+ * support appname will also not support GSSAPI
+ * encryption, so this check goes before that check.
+ * See comment below.
+ */
+ const char *sqlstate;
+
+ sqlstate = PQresultErrorField(res, PG_DIAG_SQLSTATE);
+ if (sqlstate &&
+ strcmp(sqlstate, ERRCODE_UNKNOWN_PARAM) == 0)
+ {
+ OM_uint32 minor;
+
+ PQclear(res);
+ conn->gss_disable_enc = true;
+ /* Must drop the old connection */
+ pqDropConnection(conn);
+ conn->status = CONNECTION_NEEDED;
+ gss_delete_sec_context(&minor, &conn->gctx,
+   GSS_C_NO_BUFFER);
+ goto keep_going;
+ }
+ }
+#endif
  else if (conn->send_appname &&
  (conn->appname || conn->fbappname))
  {
@@ -2569,7 +2612,7 @@ keep_going: /* We will come back to here until there is
 
  sqlstate = PQresultErrorField(res, PG_DIAG_SQLSTATE);
  if (sqlstate &&
- strcmp(sqlstate, ERRCODE_APPNAME_UNKNOWN) == 0)
+ strcmp(sqlstate, ERRCODE_UNKNOWN_PARAM) == 0)
  {
  PQclear(res);
  conn->send_appname = false;
@@ -2579,6 +2622,15 @@ keep_going: /* We will come back to here until there is
  goto keep_going;
  }
  }
+#ifdef ENABLE_GSS
+ else if (*conn->gss_enc_require == '1')
+ /*
+ * It has been determined that appname was not the
+ * cause of connection failure, so give up.
+ */
+ appendPQExpBufferStr(&conn->errorMessage,
+ libpq_gettext("Server does not support required GSS encryption\n"));
+#endif
 
  /*
  * if the resultStatus is FATAL, then conn->errorMessage
diff --git a/src/interfaces/libpq/fe-gss.c b/src/interfaces/libpq/fe-gss.c
new file mode 100644
index 0000000..4ac52e5
--- /dev/null
+++ b/src/interfaces/libpq/fe-gss.c
@@ -0,0 +1,280 @@
+/*-------------------------------------------------------------------------
+ *
+ * fe-gss.c
+ *  functions for GSSAPI support in the frontend.
+ *
+ * Portions Copyright (c) 2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *  src/backend/libpq/fe-gss.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "libpq-fe.h"
+#include "postgres_fe.h"
+#include "fe-auth.h"
+#include "libpq-int.h"
+
+#include <assert.h>
+
+#if defined(WIN32) && !defined(WIN32_ONLY_COMPILER)
+/*
+ * MIT Kerberos GSSAPI DLL doesn't properly export the symbols for MingW
+ * that contain the OIDs required. Redefine here, values copied
+ * from src/athena/auth/krb5/src/lib/gssapi/generic/gssapi_generic.c
+ */
+static const gss_OID_desc GSS_C_NT_HOSTBASED_SERVICE_desc =
+{10, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04"};
+static GSS_DLLIMP gss_OID GSS_C_NT_HOSTBASED_SERVICE = &GSS_C_NT_HOSTBASED_SERVICE_desc;
+#endif
+
+/*
+ * Fetch all errors of a specific type and append to "str".
+ */
+static void
+pg_GSS_error_int(PQExpBuffer str, const char *mprefix,
+ OM_uint32 stat, int type)
+{
+ OM_uint32 lmin_s;
+ gss_buffer_desc lmsg;
+ OM_uint32 msg_ctx = 0;
+
+ do
+ {
+ gss_display_status(&lmin_s, stat, type,
+   GSS_C_NO_OID, &msg_ctx, &lmsg);
+ appendPQExpBuffer(str, "%s: %s\n", mprefix, (char *) lmsg.value);
+ gss_release_buffer(&lmin_s, &lmsg);
+ } while (msg_ctx);
+}
+
+/*
+ * GSSAPI errors contain two parts; put both into conn->errorMessage.
+ */
+void
+pg_GSS_error(const char *mprefix, PGconn *conn,
+ OM_uint32 maj_stat, OM_uint32 min_stat)
+{
+ resetPQExpBuffer(&conn->errorMessage);
+
+ /* Fetch major error codes */
+ pg_GSS_error_int(&conn->errorMessage, mprefix, maj_stat, GSS_C_GSS_CODE);
+
+ /* Add the minor codes as well */
+ pg_GSS_error_int(&conn->errorMessage, mprefix, min_stat, GSS_C_MECH_CODE);
+}
+
+/*
+ * Continue GSS authentication with next token as needed.
+ */
+int
+pg_GSS_continue(PGconn *conn)
+{
+ OM_uint32 maj_stat,
+ min_stat,
+ lmin_s;
+
+ maj_stat = gss_init_sec_context(&min_stat,
+ GSS_C_NO_CREDENTIAL,
+ &conn->gctx,
+ conn->gtarg_nam,
+ GSS_C_NO_OID,
+ GSS_C_MUTUAL_FLAG,
+ 0,
+ GSS_C_NO_CHANNEL_BINDINGS,
+  (conn->gctx == GSS_C_NO_CONTEXT) ? GSS_C_NO_BUFFER : &conn->ginbuf,
+ NULL,
+ &conn->goutbuf,
+ NULL,
+ NULL);
+
+ if (conn->gctx != GSS_C_NO_CONTEXT)
+ {
+ free(conn->ginbuf.value);
+ conn->ginbuf.value = NULL;
+ conn->ginbuf.length = 0;
+ }
+
+ if (conn->goutbuf.length != 0)
+ {
+ /*
+ * GSS generated data to send to the server. We don't care if it's the
+ * first or subsequent packet, just send the same kind of password
+ * packet.
+ */
+ if (pqPacketSend(conn, 'p',
+ conn->goutbuf.value, conn->goutbuf.length)
+ != STATUS_OK)
+ {
+ gss_release_buffer(&lmin_s, &conn->goutbuf);
+ return STATUS_ERROR;
+ }
+ }
+ gss_release_buffer(&lmin_s, &conn->goutbuf);
+
+ if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
+ {
+ pg_GSS_error(libpq_gettext("GSSAPI continuation error"),
+ conn,
+ maj_stat, min_stat);
+ gss_release_name(&lmin_s, &conn->gtarg_nam);
+ if (conn->gctx)
+ gss_delete_sec_context(&lmin_s, &conn->gctx, GSS_C_NO_BUFFER);
+ return STATUS_ERROR;
+ }
+
+ if (maj_stat == GSS_S_COMPLETE)
+ gss_release_name(&lmin_s, &conn->gtarg_nam);
+
+ return STATUS_OK;
+}
+
+/*
+ * Send initial GSS authentication token
+ */
+int
+pg_GSS_startup(PGconn *conn)
+{
+ OM_uint32 maj_stat,
+ min_stat;
+ int maxlen;
+ gss_buffer_desc temp_gbuf;
+
+ if (!(conn->pghost && conn->pghost[0] != '\0'))
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+  libpq_gettext("host name must be specified\n"));
+ return STATUS_ERROR;
+ }
+
+ if (conn->gctx)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("duplicate GSS authentication request\n"));
+ return STATUS_ERROR;
+ }
+
+ /*
+ * Import service principal name so the proper ticket can be acquired by
+ * the GSSAPI system.
+ */
+ maxlen = NI_MAXHOST + strlen(conn->krbsrvname) + 2;
+ temp_gbuf.value = (char *) malloc(maxlen);
+ if (!temp_gbuf.value)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+  libpq_gettext("out of memory\n"));
+ return STATUS_ERROR;
+ }
+ snprintf(temp_gbuf.value, maxlen, "%s@%s",
+ conn->krbsrvname, conn->pghost);
+ temp_gbuf.length = strlen(temp_gbuf.value);
+
+ maj_stat = gss_import_name(&min_stat, &temp_gbuf,
+   GSS_C_NT_HOSTBASED_SERVICE, &conn->gtarg_nam);
+ free(temp_gbuf.value);
+
+ if (maj_stat != GSS_S_COMPLETE)
+ {
+ pg_GSS_error(libpq_gettext("GSSAPI name import error"),
+ conn,
+ maj_stat, min_stat);
+ return STATUS_ERROR;
+ }
+
+ /*
+ * Initial packet is the same as a continuation packet with no initial
+ * context.
+ */
+ conn->gctx = GSS_C_NO_CONTEXT;
+
+ return pg_GSS_continue(conn);
+}
+
+ssize_t
+pggss_inplace_decrypt(PGconn *conn, int gsslen)
+{
+ OM_uint32 major, minor;
+ gss_buffer_desc input, output;
+ ssize_t n;
+ int conf;
+
+ input.length = gsslen;
+ input.value = conn->inBuffer + conn->inCursor;
+ output.length = 0;
+ output.value = NULL;
+
+ major = gss_unwrap(&minor, conn->gctx, &input, &output, &conf, NULL);
+ if (GSS_ERROR(major))
+ {
+ pg_GSS_error("GSSAPI unwrap error", conn, major, minor);
+ return -1;
+ }
+ else if (conf == 0)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+  libpq_gettext(
+  "received GSSAPI message without confidentiality\n"));
+ return -1;
+ }
+
+ memcpy(conn->inBuffer + conn->inStart, output.value, output.length);
+ n = output.length;
+ gss_release_buffer(&minor, &output);
+ return n;
+}
+
+int
+pggss_encrypt(PGconn *conn)
+{
+ OM_uint32 major, minor;
+ gss_buffer_desc input, output;
+ int msgLen, conf;
+ uint32 len_n;
+
+ if (conn->gss_disable_enc || !conn->gctx || !conn->gss_auth_done)
+ return 0;
+ assert(conn->outMsgStart > 0);
+
+ /* We need to encrypt message type as well */
+ conn->outMsgStart -= 1;
+ msgLen = conn->outMsgEnd - conn->outMsgStart;
+
+ input.value = conn->outBuffer + conn->outMsgStart;
+ input.length = msgLen;
+ output.length = 0;
+ output.value = NULL;
+
+ major = gss_wrap(&minor, conn->gctx, 1, GSS_C_QOP_DEFAULT, &input, &conf,
+ &output);
+ if (GSS_ERROR(major))
+ {
+ pg_GSS_error("GSSAPI wrap error", conn, major, minor);
+ return -1;
+ }
+ else if (conf == 0)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+  libpq_gettext(
+  "Failed to obtain confidentiality for outgoing GSSAPI message\n"));
+ return -1;
+ }
+
+ msgLen = output.length + 4;
+ if (pqCheckOutBufferSpace(conn->outMsgStart + msgLen + 1, conn))
+ return -1;
+
+ conn->outBuffer[conn->outMsgStart] = 'g'; /* GSSAPI message */
+
+ len_n = htonl(msgLen);
+ memcpy(conn->outBuffer + conn->outMsgStart + 1, &len_n, 4);
+
+ memcpy(conn->outBuffer + conn->outMsgStart + 1 + 4,
+   output.value, output.length);
+ conn->outMsgEnd = conn->outMsgStart + msgLen + 1;
+
+ gss_release_buffer(&minor, &output);
+ return msgLen + 1;
+}
diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c
index 0dbcf73..2379aff 100644
--- a/src/interfaces/libpq/fe-misc.c
+++ b/src/interfaces/libpq/fe-misc.c
@@ -604,6 +604,11 @@ pqPutMsgEnd(PGconn *conn)
  memcpy(conn->outBuffer + conn->outMsgStart, &msgLen, 4);
  }
 
+#ifdef ENABLE_GSS
+ if (pggss_encrypt(conn) < 0)
+ return EOF;
+#endif
+
  /* Make message eligible to send */
  conn->outCount = conn->outMsgEnd;
 
diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c
index 641804c..6127b91 100644
--- a/src/interfaces/libpq/fe-protocol3.c
+++ b/src/interfaces/libpq/fe-protocol3.c
@@ -129,6 +129,58 @@ pqParseInput3(PGconn *conn)
  return;
  }
 
+#ifdef ENABLE_GSS
+ /* We want to be ready in both IDLE and BUSY states for encryption */
+ if (id == 'g' && !conn->gss_disable_enc && conn->gctx)
+ {
+ ssize_t encEnd, next;
+
+ encEnd = pggss_inplace_decrypt(conn, msgLength);
+ if (encEnd <= 0)
+ {
+ /* error message placed by pggss_inplace_decrypt() */
+ pqSaveErrorResult(conn);
+ conn->asyncStatus = PGASYNC_READY;
+ pqDropConnection(conn);
+ conn->status = CONNECTION_BAD;
+ return;
+ }
+
+ /* shift contents of buffer to account for slack */
+ encEnd += conn->inStart;
+ next = conn->inStart + msgLength + 5;
+ memmove(conn->inBuffer + encEnd, conn->inBuffer + next,
+ conn->inEnd - next);
+ conn->inEnd = (conn->inEnd - next) + encEnd;
+
+ conn->inCursor = conn->inStart;
+ (void) pqGetc(&id, conn);
+ (void) pqGetInt(&msgLength, 4, conn);
+ msgLength -= 4;
+ if (msgLength != encEnd - conn->inCursor)
+ {
+ /* This isn't a sync error because decrypt was successful */
+ printfPQExpBuffer(&conn->errorMessage,
+  libpq_gettext(
+  "server lied about message length: got message length %ld, but expected legnth %d\n"),
+  encEnd - conn->inCursor, msgLength);
+ /* build an error result holding the error message */
+ pqSaveErrorResult(conn);
+ /* drop out of GetResult wait loop */
+ conn->asyncStatus = PGASYNC_READY;
+
+ pqDropConnection(conn);
+ /* No more connection to backend */
+ conn->status = CONNECTION_BAD;
+ }
+ conn->gss_decrypted_cur = true;
+ }
+ else if (!conn->gss_disable_enc && conn->gss_auth_done &&
+ !conn->gss_decrypted_cur && id != 'E')
+ /* This could be a sync error, so let's handle it as such. */
+ handleSyncLoss(conn, id, msgLength);
+#endif
+
  /*
  * NOTIFY and NOTICE messages can happen in any state; always process
  * them right away.
@@ -415,6 +467,9 @@ pqParseInput3(PGconn *conn)
  {
  /* Normal case: parsing agrees with specified length */
  conn->inStart = conn->inCursor;
+#ifdef ENABLE_GSS
+ conn->gss_decrypted_cur = false;
+#endif
  }
  else
  {
@@ -2083,6 +2138,11 @@ build_startup_packet(const PGconn *conn, char *packet,
  if (conn->client_encoding_initial && conn->client_encoding_initial[0])
  ADD_STARTUP_OPTION("client_encoding", conn->client_encoding_initial);
 
+#ifdef ENABLE_GSS
+ if (!conn->gss_disable_enc)
+ ADD_STARTUP_OPTION("gss_encrypt", "on");
+#endif
+
  /* Add any environment-driven GUC settings needed */
  for (next_eo = options; next_eo->envName; next_eo++)
  {
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 04d056e..cca90c3 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -446,6 +446,10 @@ struct pg_conn
  gss_name_t gtarg_nam; /* GSS target name */
  gss_buffer_desc ginbuf; /* GSS input token */
  gss_buffer_desc goutbuf; /* GSS output token */
+ bool gss_disable_enc; /* Does server recognize gss_encrypt? */
+ bool gss_auth_done; /* Did we finish the AUTH step? */
+ bool gss_decrypted_cur; /* Is first message in buffer decrypted? */
+ char *gss_enc_require; /* Can we downgrade to plaintext? */
 #endif
 
 #ifdef ENABLE_SSPI
@@ -643,6 +647,18 @@ extern bool pgtls_read_pending(PGconn *conn);
 extern ssize_t pgtls_write(PGconn *conn, const void *ptr, size_t len);
 
 /*
+ * GSSAPI functions defined in fe-gss.c
+ */
+#ifdef ENABLE_GSS
+extern ssize_t pggss_inplace_decrypt(PGconn *conn, int gsslen);
+extern int pggss_encrypt(PGconn *conn);
+extern void pg_GSS_error(const char *mprefix, PGconn *conn, OM_uint32 maj_stat,
+ OM_uint32 min_stat);
+extern int pg_GSS_startup(PGconn *conn);
+extern int pg_GSS_continue(PGconn *conn);
+#endif /* ENABLE_GSS */
+
+/*
  * this is so that we can check if a connection is non-blocking internally
  * without the overhead of a function call
  */
--
2.6.1


signature.asc (834 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH v3] GSSAPI encryption support

Michael Paquier
On Wed, Oct 14, 2015 at 7:34 AM, Robbie Harwood <[hidden email]> wrote:

> Alright, here's v3.  As requested, it's one patch now.  Other things
> addressed herein include:
> Essentially, the problem is that socket_putmessage_noblock() needs to
> know the size of the message to put in the buffer but we can't know
> that until we've encrypted the message.  socket_putmessage_noblock()
> calls socket_putmessage() after ensuring the call will not block;
> however, other code paths simply call directly into socket_putmessage()
> and so socket_putmessage() needs to have a path to encryption as well.
>
> If you have other potential solutions to this problem, I would love to
> hear them; right now though I don't see a better way.
>
> Patch follows.  Thanks!

After giving a quick shot at this patch, I am still seeing the same problem:
psql: lost synchronization with server: got message type "S", length 22
(And unpatched works, I will try to put my head into your code...)
Regards,
--
Michael


--
Sent via pgsql-hackers mailing list ([hidden email])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH v3] GSSAPI encryption support

Craig Ringer-3
In reply to this post by Robbie Harwood
On 14 October 2015 at 06:34, Robbie Harwood <[hidden email]> wrote:
> Alright, here's v3.  As requested, it's one patch now.

I hate to ask, but have you looked at how this interacts with Windows?

We support Windows SSPI (on a domain-member host) authenticating to a
PostgreSQL server using gssapi with spnego.

We also support a PostgreSQL client on *nix authenticating using
gssapi with spnego to a PostgreSQL server that's requesting sspi mode.

The relevant code is all a bit tangled, since there's support in there
for using Kerberos libraries on Windows instead of SSPI too. I doubt
anybody uses that last one, tests it, or cares about it, though, given
the painful hoop-jumping, registry key permission changes, etc
required to make it work.

For bonus fun, RC4, DES, AES128 or AES256 are available/used for
Kerberos encryption on Windows. See
http://blogs.msdn.com/b/openspecification/archive/2011/05/31/windows-configurations-for-kerberos-supported-encryption-type.aspx
. Though given that Win7 defaults to AES256 it's probably reasonable
to simply not care about anything else.


--
 Craig Ringer                   http://www.2ndQuadrant.com/
 PostgreSQL Development, 24x7 Support, Training & Services


--
Sent via pgsql-hackers mailing list ([hidden email])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
1234 ... 11