From d5b973752968f87c9bb2ff9434d523657eb4ba67 Mon Sep 17 00:00:00 2001
From: "Robbie Harwood (frozencemetery)" <rharwood@redhat.com>
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

