0002-fix-server-gss-issues.patch
text/x-diff
Filename: 0002-fix-server-gss-issues.patch
Type: text/x-diff
Part: 1
Patch
Format: unified
Series: patch 0002
| File | + | − |
|---|---|---|
| src/backend/libpq/auth.c | 21 | 3 |
| src/backend/libpq/be-gssapi-common.c | 13 | 5 |
| src/backend/libpq/be-secure.c | 2 | 2 |
| src/backend/libpq/be-secure-gssapi.c | 72 | 26 |
| src/backend/libpq/hba.c | 4 | 2 |
| src/backend/libpq/pqcomm.c | 12 | 15 |
| src/backend/postmaster/pgstat.c | 9 | 3 |
| src/backend/postmaster/postmaster.c | 4 | 33 |
| src/backend/utils/init/postinit.c | 16 | 5 |
| src/include/libpq/libpq-be.h | 3 | 2 |
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