diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 3d80930968..7f65310c14 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -383,7 +383,7 @@ ClientAuthentication(Port *port)
 	}
 
 #ifdef ENABLE_GSS
-	if (port->gss->enc && port->hba->auth_method != uaReject &&
+	if (port->gss && port->gss->enc && port->hba->auth_method != uaReject &&
 		port->hba->auth_method != uaImplicitReject &&
 		port->hba->auth_method != uaTrust &&
 		port->hba->auth_method != uaGSS)
@@ -533,7 +533,17 @@ ClientAuthentication(Port *port)
 
 		case uaGSS:
 #ifdef ENABLE_GSS
+			/* We might or might not have the gss workspace already */
+			if (port->gss == NULL)
+				port->gss = (pg_gssinfo *)
+					MemoryContextAllocZero(TopMemoryContext,
+										   sizeof(pg_gssinfo));
 			port->gss->auth = true;
+
+			/*
+			 * If GSS state was set up while enabling encryption, we can just
+			 * check the client's principal.  Otherwise, ask for it.
+			 */
 			if (port->gss->enc)
 				status = pg_GSS_checkauth(port);
 			else
@@ -548,6 +558,10 @@ ClientAuthentication(Port *port)
 
 		case uaSSPI:
 #ifdef ENABLE_SSPI
+			if (port->gss == NULL)
+				port->gss = (pg_gssinfo *)
+					MemoryContextAllocZero(TopMemoryContext,
+										   sizeof(pg_gssinfo));
 			sendAuthRequest(port, AUTH_REQ_SSPI, NULL, 0);
 			status = pg_SSPI_recvauth(port);
 #else
@@ -1185,9 +1199,10 @@ pg_GSS_recvauth(Port *port)
 		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,
+			pg_GSS_error(COMMERROR,
 						 _("accepting GSS security context failed"),
 						 maj_stat, min_stat);
+			return STATUS_ERROR;
 		}
 
 		if (maj_stat == GSS_S_CONTINUE_NEEDED)
@@ -1224,9 +1239,12 @@ pg_GSS_checkauth(Port *port)
 	 */
 	maj_stat = gss_display_name(&min_stat, port->gss->name, &gbuf, NULL);
 	if (maj_stat != GSS_S_COMPLETE)
-		pg_GSS_error(ERROR,
+	{
+		pg_GSS_error(COMMERROR,
 					 _("retrieving GSS user name failed"),
 					 maj_stat, min_stat);
+		return STATUS_ERROR;
+	}
 
 	/*
 	 * Copy the original name of the authenticated principal into our backend
diff --git a/src/backend/libpq/be-gssapi-common.c b/src/backend/libpq/be-gssapi-common.c
index ef9b8ebd6d..a7006a0ab7 100644
--- a/src/backend/libpq/be-gssapi-common.c
+++ b/src/backend/libpq/be-gssapi-common.c
@@ -42,14 +42,22 @@ pg_GSS_error_int(char *s, size_t len, OM_uint32 stat, int type)
 	while (msg_ctx && i < len);
 
 	if (msg_ctx || i == len)
-		ereport(WARNING,
-				(errmsg_internal("incomplete GSS error report")));
+		elog(COMMERROR, "incomplete GSS error report");
 }
 
 /*
- * Fetch and report all error messages from GSSAPI.  To avoid allocation,
- * total error size is capped (at 128 bytes for each of major and minor).  No
- * known mechanisms will produce error messages beyond this cap.
+ * Report the GSSAPI error described by maj_stat/min_stat.
+ *
+ * errmsg should be an already-translated primary error message.
+ * The GSSAPI info is appended as errdetail.
+ *
+ * Caution: although we allow the caller to specify the ereport severity,
+ * passing anything but COMMERROR is almost certainly wrong, as it'd lead
+ * to infinite recursion when elog.c tries to write to the client.
+ *
+ * To avoid memory allocation, total error size is capped (at 128 bytes for
+ * each of major and minor).  No known mechanisms will produce error messages
+ * beyond this cap.
  */
 void
 pg_GSS_error(int severity, const char *errmsg,
diff --git a/src/backend/libpq/be-secure-gssapi.c b/src/backend/libpq/be-secure-gssapi.c
index 5a73302b7b..b70d74ca83 100644
--- a/src/backend/libpq/be-secure-gssapi.c
+++ b/src/backend/libpq/be-secure-gssapi.c
@@ -21,6 +21,7 @@
 #include "libpq/pqformat.h"
 #include "miscadmin.h"
 #include "pgstat.h"
+#include "utils/memutils.h"
 
 
 /*
@@ -81,10 +82,14 @@ static uint32 PqGSSMaxPktSize;	/* Maximum size we can encrypt and fit the
  * transport negotiation is complete).
  *
  * On success, returns the number of data bytes consumed (possibly less than
- * len).  On failure, returns -1 with errno set appropriately.  (For fatal
- * errors, we may just elog and exit, if errno wouldn't be sufficient to
- * describe the error.)  For retryable errors, caller should call again
- * (passing the same data) once the socket is ready.
+ * len).  On failure, returns -1 with errno set appropriately.  For retryable
+ * errors, caller should call again (passing the same data) once the socket
+ * is ready.
+ *
+ * Dealing with fatal errors here is a bit tricky: we can't invoke elog(FATAL)
+ * since it would try to write to the client, probably resulting in infinite
+ * recursion.  Instead, use elog(COMMERROR) to log extra info about the
+ * failure if necessary, and then return an errno indicating connection loss.
  */
 ssize_t
 be_gssapi_write(Port *port, void *ptr, size_t len)
@@ -108,8 +113,11 @@ be_gssapi_write(Port *port, void *ptr, size_t len)
 	 * again, so if it offers a len less than that, something is wrong.
 	 */
 	if (len < PqGSSSendConsumed)
-		elog(FATAL, "GSSAPI caller failed to retransmit all data needing to be retried");
-
+	{
+		elog(COMMERROR, "GSSAPI caller failed to retransmit all data needing to be retried");
+		errno = ECONNRESET;
+		return -1;
+	}
 	/* Discount whatever source data we already encrypted. */
 	bytes_to_encrypt = len - PqGSSSendConsumed;
 	bytes_encrypted = PqGSSSendConsumed;
@@ -192,17 +200,27 @@ be_gssapi_write(Port *port, void *ptr, size_t len)
 		major = gss_wrap(&minor, gctx, 1, GSS_C_QOP_DEFAULT,
 						 &input, &conf_state, &output);
 		if (major != GSS_S_COMPLETE)
-			pg_GSS_error(FATAL, gettext_noop("GSSAPI wrap error"), major, minor);
-
+		{
+			pg_GSS_error(COMMERROR, _("GSSAPI wrap error"), major, minor);
+			errno = ECONNRESET;
+			return -1;
+		}
 		if (conf_state == 0)
-			ereport(FATAL,
+		{
+			ereport(COMMERROR,
 					(errmsg("outgoing GSSAPI message would not use confidentiality")));
-
+			errno = ECONNRESET;
+			return -1;
+		}
 		if (output.length > PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))
-			ereport(FATAL,
+		{
+			ereport(COMMERROR,
 					(errmsg("server tried to send oversize GSSAPI packet (%zu > %zu)",
 							(size_t) output.length,
 							PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))));
+			errno = ECONNRESET;
+			return -1;
+		}
 
 		bytes_encrypted += input.length;
 		bytes_to_encrypt -= input.length;
@@ -234,9 +252,11 @@ be_gssapi_write(Port *port, void *ptr, size_t len)
  * transport negotiation is complete).
  *
  * Returns the number of data bytes read, or on failure, returns -1
- * with errno set appropriately.  (For fatal errors, we may just elog and
- * exit, if errno wouldn't be sufficient to describe the error.)  For
- * retryable errors, caller should call again once the socket is ready.
+ * with errno set appropriately.  For retryable errors, caller should call
+ * again once the socket is ready.
+ *
+ * We treat fatal errors the same as in be_gssapi_write(), even though the
+ * argument about infinite recursion doesn't apply here.
  */
 ssize_t
 be_gssapi_read(Port *port, void *ptr, size_t len)
@@ -326,10 +346,14 @@ be_gssapi_read(Port *port, void *ptr, size_t len)
 		input.length = pg_ntoh32(*(uint32 *) PqGSSRecvBuffer);
 
 		if (input.length > PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32))
-			ereport(FATAL,
+		{
+			ereport(COMMERROR,
 					(errmsg("oversize GSSAPI packet sent by the client (%zu > %zu)",
 							(size_t) input.length,
 							PQ_GSS_RECV_BUFFER_SIZE - sizeof(uint32))));
+			errno = ECONNRESET;
+			return -1;
+		}
 
 		/*
 		 * Read as much of the packet as we are able to on this call into
@@ -361,12 +385,18 @@ be_gssapi_read(Port *port, void *ptr, size_t len)
 
 		major = gss_unwrap(&minor, gctx, &input, &output, &conf_state, NULL);
 		if (major != GSS_S_COMPLETE)
-			pg_GSS_error(FATAL, gettext_noop("GSSAPI unwrap error"),
-						 major, minor);
-
+		{
+			pg_GSS_error(COMMERROR, _("GSSAPI unwrap error"), major, minor);
+			errno = ECONNRESET;
+			return -1;
+		}
 		if (conf_state == 0)
-			ereport(FATAL,
+		{
+			ereport(COMMERROR,
 					(errmsg("incoming GSSAPI message did not use confidentiality")));
+			errno = ECONNRESET;
+			return -1;
+		}
 
 		memcpy(PqGSSResultBuffer, output.value, output.length);
 		PqGSSResultLength = output.length;
@@ -468,6 +498,12 @@ secure_open_gssapi(Port *port)
 	OM_uint32	major,
 				minor;
 
+	/*
+	 * Allocate subsidiary Port data for GSSAPI operations.
+	 */
+	port->gss = (pg_gssinfo *)
+		MemoryContextAllocZero(TopMemoryContext, sizeof(pg_gssinfo));
+
 	/*
 	 * Allocate buffers and initialize state variables.  By malloc'ing the
 	 * buffers at this point, we avoid wasting static data space in processes
@@ -521,10 +557,13 @@ secure_open_gssapi(Port *port)
 		 * Verify on our side that the client doesn't do something funny.
 		 */
 		if (input.length > PQ_GSS_RECV_BUFFER_SIZE)
-			ereport(FATAL,
+		{
+			ereport(COMMERROR,
 					(errmsg("oversize GSSAPI packet sent by the client (%zu > %d)",
 							(size_t) input.length,
 							PQ_GSS_RECV_BUFFER_SIZE)));
+			return -1;
+		}
 
 		/*
 		 * Get the rest of the packet so we can pass it to GSSAPI to accept
@@ -544,7 +583,7 @@ secure_open_gssapi(Port *port)
 									   NULL, NULL);
 		if (GSS_ERROR(major))
 		{
-			pg_GSS_error(ERROR, gettext_noop("could not accept GSSAPI security context"),
+			pg_GSS_error(COMMERROR, _("could not accept GSSAPI security context"),
 						 major, minor);
 			gss_release_buffer(&minor, &output);
 			return -1;
@@ -570,10 +609,14 @@ secure_open_gssapi(Port *port)
 			uint32		netlen = pg_hton32(output.length);
 
 			if (output.length > PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))
-				ereport(FATAL,
+			{
+				ereport(COMMERROR,
 						(errmsg("server tried to send oversize GSSAPI packet (%zu > %zu)",
 								(size_t) output.length,
 								PQ_GSS_SEND_BUFFER_SIZE - sizeof(uint32))));
+				gss_release_buffer(&minor, &output);
+				return -1;
+			}
 
 			memcpy(PqGSSSendBuffer, (char *) &netlen, sizeof(uint32));
 			PqGSSSendLength += sizeof(uint32);
@@ -634,8 +677,10 @@ secure_open_gssapi(Port *port)
 								&PqGSSMaxPktSize);
 
 	if (GSS_ERROR(major))
-		pg_GSS_error(FATAL, gettext_noop("GSSAPI size check error"),
-					 major, minor);
+	{
+		pg_GSS_error(COMMERROR, _("GSSAPI size check error"), major, minor);
+		return -1;
+	}
 
 	port->gss->enc = true;
 
@@ -667,12 +712,13 @@ be_gssapi_get_enc(Port *port)
 }
 
 /*
- * Return the GSSAPI principal used for authentication on this connection.
+ * Return the GSSAPI principal used for authentication on this connection
+ * (NULL if we did not perform GSSAPI authentication).
  */
 const char *
 be_gssapi_get_princ(Port *port)
 {
-	if (!port || !port->gss->auth)
+	if (!port || !port->gss)
 		return NULL;
 
 	return port->gss->princ;
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index 2ae507a902..59bc02e79c 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -160,7 +160,7 @@ retry:
 	else
 #endif
 #ifdef ENABLE_GSS
-	if (port->gss->enc)
+	if (port->gss && port->gss->enc)
 	{
 		n = be_gssapi_read(port, ptr, len);
 		waitfor = WL_SOCKET_READABLE;
@@ -273,7 +273,7 @@ retry:
 	else
 #endif
 #ifdef ENABLE_GSS
-	if (port->gss->enc)
+	if (port->gss && port->gss->enc)
 	{
 		n = be_gssapi_write(port, ptr, len);
 		waitfor = WL_SOCKET_WRITEABLE;
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index 0cc4397769..41ca0791ea 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -2134,9 +2134,11 @@ check_hba(hbaPort *port)
 
 			/* Check GSSAPI state */
 #ifdef ENABLE_GSS
-			if (port->gss->enc && hba->conntype == ctHostNoGSS)
+			if (port->gss && port->gss->enc &&
+				hba->conntype == ctHostNoGSS)
 				continue;
-			else if (!port->gss->enc && hba->conntype == ctHostGSS)
+			else if (!(port->gss && port->gss->enc) &&
+					 hba->conntype == ctHostGSS)
 				continue;
 #else
 			if (hba->conntype == ctHostGSS)
diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c
index 0b511008fc..3ea7c6167e 100644
--- a/src/backend/libpq/pqcomm.c
+++ b/src/backend/libpq/pqcomm.c
@@ -256,29 +256,26 @@ socket_close(int code, Datum arg)
 	/* Nothing to do in a standalone backend, where MyProcPort is NULL. */
 	if (MyProcPort != NULL)
 	{
-#if defined(ENABLE_GSS) || defined(ENABLE_SSPI)
 #ifdef ENABLE_GSS
-		OM_uint32	min_s;
-
 		/*
 		 * Shutdown GSSAPI layer.  This section does nothing when interrupting
 		 * BackendInitialize(), because pg_GSS_recvauth() makes first use of
 		 * "ctx" and "cred".
+		 *
+		 * Note that we don't bother to free MyProcPort->gss, since we're
+		 * about to exit anyway.
 		 */
-		if (MyProcPort->gss->ctx != GSS_C_NO_CONTEXT)
-			gss_delete_sec_context(&min_s, &MyProcPort->gss->ctx, NULL);
+		if (MyProcPort->gss)
+		{
+			OM_uint32	min_s;
 
-		if (MyProcPort->gss->cred != GSS_C_NO_CREDENTIAL)
-			gss_release_cred(&min_s, &MyProcPort->gss->cred);
-#endif							/* ENABLE_GSS */
+			if (MyProcPort->gss->ctx != GSS_C_NO_CONTEXT)
+				gss_delete_sec_context(&min_s, &MyProcPort->gss->ctx, NULL);
 
-		/*
-		 * GSS and SSPI share the port->gss struct.  Since nowhere else does a
-		 * postmaster child free this, doing so is safe when interrupting
-		 * BackendInitialize().
-		 */
-		free(MyProcPort->gss);
-#endif							/* ENABLE_GSS || ENABLE_SSPI */
+			if (MyProcPort->gss->cred != GSS_C_NO_CREDENTIAL)
+				gss_release_cred(&min_s, &MyProcPort->gss->cred);
+		}
+#endif							/* ENABLE_GSS */
 
 		/*
 		 * Cleanly shut down SSL layer.  Nowhere else does a postmaster child
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 71f2b90ca8..7fba907479 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -2888,6 +2888,11 @@ BackendStatusShmemSize(void)
 	/* BackendSslStatusBuffer: */
 	size = add_size(size,
 					mul_size(sizeof(PgBackendSSLStatus), NumBackendStatSlots));
+#endif
+#ifdef ENABLE_GSS
+	/* BackendGssStatusBuffer: */
+	size = add_size(size,
+					mul_size(sizeof(PgBackendGSSStatus), NumBackendStatSlots));
 #endif
 	return size;
 }
@@ -3172,12 +3177,13 @@ pgstat_bestart(void)
 #ifdef ENABLE_GSS
 	if (MyProcPort && MyProcPort->gss != NULL)
 	{
+		const char *princ = be_gssapi_get_princ(MyProcPort);
+
 		lbeentry.st_gss = true;
 		lgssstatus.gss_auth = be_gssapi_get_auth(MyProcPort);
 		lgssstatus.gss_enc = be_gssapi_get_enc(MyProcPort);
-
-		if (lgssstatus.gss_auth)
-			strlcpy(lgssstatus.gss_princ, be_gssapi_get_princ(MyProcPort), NAMEDATALEN);
+		if (princ)
+			strlcpy(lgssstatus.gss_princ, princ, NAMEDATALEN);
 	}
 	else
 	{
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index bf883184b1..0bb85a18f6 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -2063,6 +2063,7 @@ retry1:
 	else if (proto == NEGOTIATE_GSS_CODE && !gss_done)
 	{
 		char		GSSok = 'N';
+
 #ifdef ENABLE_GSS
 		/* No GSSAPI encryption when on Unix socket */
 		if (!IS_AF_UNIX(port->laddr.addr.ss_family))
@@ -2531,37 +2532,19 @@ ConnCreate(int serverFd)
 		return NULL;
 	}
 
-	/*
-	 * Allocate GSSAPI specific state struct
-	 */
-#ifndef EXEC_BACKEND
-#if defined(ENABLE_GSS) || defined(ENABLE_SSPI)
-	port->gss = (pg_gssinfo *) calloc(1, sizeof(pg_gssinfo));
-	if (!port->gss)
-	{
-		ereport(LOG,
-				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("out of memory")));
-		ExitPostmaster(1);
-	}
-#endif
-#endif
-
 	return port;
 }
 
 
 /*
  * ConnFree -- free a local connection data structure
+ *
+ * Caller has already closed the socket if any, so there's not much
+ * to do here.
  */
 static void
 ConnFree(Port *conn)
 {
-#ifdef USE_SSL
-	secure_close(conn);
-#endif
-	if (conn->gss)
-		free(conn->gss);
 	free(conn);
 }
 
@@ -4917,18 +4900,6 @@ SubPostmasterMain(int argc, char *argv[])
 	/* Setup as postmaster child */
 	InitPostmasterChild();
 
-	/*
-	 * Set up memory area for GSS information. Mirrors the code in ConnCreate
-	 * for the non-exec case.
-	 */
-#if defined(ENABLE_GSS) || defined(ENABLE_SSPI)
-	port.gss = (pg_gssinfo *) calloc(1, sizeof(pg_gssinfo));
-	if (!port.gss)
-		ereport(FATAL,
-				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("out of memory")));
-#endif
-
 	/*
 	 * If appropriate, physically re-attach to shared memory segment. We want
 	 * to do this before going any further to ensure that we can attach at the
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 82d451569d..9723e457ce 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -270,11 +270,22 @@ PerformAuthentication(Port *port)
 							 be_tls_get_compression(port) ? _("on") : _("off"));
 #endif
 #ifdef ENABLE_GSS
-		if (be_gssapi_get_princ(port))
-			appendStringInfo(&logmsg, _(" GSS (authenticated=%s, encrypted=%s, principal=%s)"),
-							 be_gssapi_get_auth(port) ? _("yes") : _("no"),
-							 be_gssapi_get_enc(port) ? _("yes") : _("no"),
-							 be_gssapi_get_princ(port));
+		if (port->gss)
+		{
+			const char *princ = be_gssapi_get_princ(port);
+
+			if (princ)
+				appendStringInfo(&logmsg,
+								 _(" GSS (authenticated=%s, encrypted=%s, principal=%s)"),
+								 be_gssapi_get_auth(port) ? _("yes") : _("no"),
+								 be_gssapi_get_enc(port) ? _("yes") : _("no"),
+								 princ);
+			else
+				appendStringInfo(&logmsg,
+								 _(" GSS (authenticated=%s, encrypted=%s)"),
+								 be_gssapi_get_auth(port) ? _("yes") : _("no"),
+								 be_gssapi_get_enc(port) ? _("yes") : _("no"));
+		}
 #endif
 
 		ereport(LOG, errmsg_internal("%s", logmsg.data));
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 0a23281ad5..fa778e1192 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -176,8 +176,9 @@ typedef struct Port
 #if defined(ENABLE_GSS) || defined(ENABLE_SSPI)
 
 	/*
-	 * If GSSAPI is supported, store GSSAPI information. Otherwise, store a
-	 * NULL pointer to make sure offsets in the struct remain the same.
+	 * If GSSAPI is supported and used on this connection, store GSSAPI
+	 * information.  Even when GSSAPI is not compiled in, store a NULL pointer
+	 * to keep struct offsets the same (for extension ABI compatibility).
 	 */
 	pg_gssinfo *gss;
 #else
