diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/libpq/auth.c | 35 | ||||
-rw-r--r-- | src/backend/libpq/be-gssapi-common.c | 51 | ||||
-rw-r--r-- | src/backend/libpq/be-secure-gssapi.c | 98 | ||||
-rw-r--r-- | src/backend/libpq/be-secure.c | 4 | ||||
-rw-r--r-- | src/backend/libpq/hba.c | 19 | ||||
-rw-r--r-- | src/backend/libpq/pqcomm.c | 27 | ||||
-rw-r--r-- | src/backend/postmaster/pgstat.c | 12 | ||||
-rw-r--r-- | src/backend/postmaster/postmaster.c | 37 | ||||
-rw-r--r-- | src/backend/utils/init/postinit.c | 21 | ||||
-rw-r--r-- | src/include/libpq/be-gssapi-common.h | 2 | ||||
-rw-r--r-- | src/include/libpq/libpq-be.h | 5 |
11 files changed, 178 insertions, 133 deletions
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c index 3d809309685..d4ec965934a 100644 --- a/src/backend/libpq/auth.c +++ b/src/backend/libpq/auth.c @@ -382,17 +382,6 @@ ClientAuthentication(Port *port) errmsg("connection requires a valid client certificate"))); } -#ifdef ENABLE_GSS - if (port->gss->enc && port->hba->auth_method != uaReject && - port->hba->auth_method != uaImplicitReject && - port->hba->auth_method != uaTrust && - port->hba->auth_method != uaGSS) - { - ereport(FATAL, (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), - errmsg("GSSAPI encryption can only be used with gss, trust, or reject authentication methods"))); - } -#endif - /* * Now proceed to do the actual authentication check */ @@ -533,7 +522,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 +547,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 +1188,9 @@ 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, - _("accepting GSS security context failed"), + pg_GSS_error(_("accepting GSS security context failed"), maj_stat, min_stat); + return STATUS_ERROR; } if (maj_stat == GSS_S_CONTINUE_NEEDED) @@ -1224,9 +1227,11 @@ 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, - _("retrieving GSS user name failed"), + { + pg_GSS_error(_("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 ef9b8ebd6d4..be5d051c202 100644 --- a/src/backend/libpq/be-gssapi-common.c +++ b/src/backend/libpq/be-gssapi-common.c @@ -17,8 +17,9 @@ #include "libpq/be-gssapi-common.h" /* - * Helper function for getting all strings of a GSSAPI error (of specified - * stat). Call once for GSS_CODE and once for MECH_CODE. + * Fetch all errors of a specific type and append to "s" (buffer of size len). + * If we obtain more than one string, separate them with spaces. + * Call once for GSS_CODE and once for MECH_CODE. */ static void pg_GSS_error_int(char *s, size_t len, OM_uint32 stat, int type) @@ -28,31 +29,49 @@ pg_GSS_error_int(char *s, size_t len, OM_uint32 stat, int type) OM_uint32 lmin_s, msg_ctx = 0; - gmsg.value = NULL; - gmsg.length = 0; + s[0] = '\0'; /* just in case gss_display_status fails */ do { - gss_display_status(&lmin_s, stat, type, - GSS_C_NO_OID, &msg_ctx, &gmsg); - strlcpy(s + i, gmsg.value, len - i); + if (gss_display_status(&lmin_s, stat, type, GSS_C_NO_OID, + &msg_ctx, &gmsg) != GSS_S_COMPLETE) + break; + if (i > 0) + { + if (i < len) + s[i] = ' '; + i++; + } + if (i < len) + strlcpy(s + i, gmsg.value, len - i); i += gmsg.length; gss_release_buffer(&lmin_s, &gmsg); } - while (msg_ctx && i < len); + while (msg_ctx); - if (msg_ctx || i == len) - ereport(WARNING, - (errmsg_internal("incomplete GSS error report"))); + if (i >= len) + { + elog(COMMERROR, "incomplete GSS error report"); + s[len - 1] = '\0'; /* ensure string is nul-terminated */ + } } /* - * 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. + * + * The error is always reported with elevel COMMERROR; we daren't try to + * send it to the client, as that'd likely 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, +pg_GSS_error(const char *errmsg, OM_uint32 maj_stat, OM_uint32 min_stat) { char msg_major[128], @@ -68,7 +87,7 @@ pg_GSS_error(int severity, const char *errmsg, * errmsg_internal, since translation of the first part must be done * before calling this function anyway. */ - ereport(severity, + ereport(COMMERROR, (errmsg_internal("%s", errmsg), errdetail_internal("%s: %s", msg_major, msg_minor))); } diff --git a/src/backend/libpq/be-secure-gssapi.c b/src/backend/libpq/be-secure-gssapi.c index 5a73302b7b9..1747fccb143 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(_("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(_("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; @@ -469,6 +499,12 @@ secure_open_gssapi(Port *port) 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 * that will never use them, and we ensure that the buffers are @@ -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(_("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(_("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 2ae507a9025..59bc02e79c6 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 0cc43977691..99319b273aa 100644 --- a/src/backend/libpq/hba.c +++ b/src/backend/libpq/hba.c @@ -1447,19 +1447,6 @@ parse_hba_line(TokenizedLine *tok_line, int elevel) *err_msg = "gssapi authentication is not supported on local sockets"; return NULL; } - if (parsedline->conntype == ctHostGSS && - parsedline->auth_method != uaGSS && - parsedline->auth_method != uaReject && - parsedline->auth_method != uaTrust) - { - ereport(elevel, - (errcode(ERRCODE_CONFIG_FILE_ERROR), - errmsg("GSSAPI encryption only supports gss, trust, or reject authentication"), - errcontext("line %d of configuration file \"%s\"", - line_num, HbaFileName))); - *err_msg = "GSSAPI encryption only supports gss, trust, or reject authentication"; - return NULL; - } if (parsedline->conntype != ctLocal && parsedline->auth_method == uaPeer) @@ -2134,9 +2121,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 0b511008fc8..3ea7c6167eb 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 d87d9d06ee2..123369f4faf 100644 --- a/src/backend/postmaster/pgstat.c +++ b/src/backend/postmaster/pgstat.c @@ -2889,6 +2889,11 @@ BackendStatusShmemSize(void) 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 fff4227e0b6..b3ccd18cda6 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -2052,6 +2052,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)) @@ -2520,37 +2521,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); } @@ -4907,18 +4890,6 @@ SubPostmasterMain(int argc, char *argv[]) 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 * same address the postmaster used. On the other hand, if we choose not diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c index 82d451569d6..9723e457ce9 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/be-gssapi-common.h b/src/include/libpq/be-gssapi-common.h index edf89675cf7..e1c23ac5459 100644 --- a/src/include/libpq/be-gssapi-common.h +++ b/src/include/libpq/be-gssapi-common.h @@ -20,7 +20,7 @@ #include <gssapi/gssapi.h> #endif -void pg_GSS_error(int severity, const char *errmsg, +extern void pg_GSS_error(const char *errmsg, OM_uint32 maj_stat, OM_uint32 min_stat); #endif /* BE_GSSAPI_COMMON_H */ diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h index 0a23281ad59..fa778e11921 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 |