connection.
SSL
sortas="libpq">with libpq
+
+ Note that if
GSSAPI encryption is possible,
+ that will be used in preference to
SSL
+ encryption, regardless of the value of sslmode .
+ To force use of
SSL encryption in an
+ environment that has working
GSSAPI
+ infrastructure (such as a Kerberos server), also
+ set gssencmode to disable .
+
This must match the service name specified in the server
configuration for Kerberos authentication to succeed. (See also
.)
+ The default value is normally postgres ,
+ but that can be changed when
+ building
PostgreSQL via
+ the --with-krb-srvnam option
+ In most environments, this parameter never needs to be changed.
+ Some Kerberos implementations might require a different service name,
+ such as Microsoft Active Directory which requires the service name
+ to be in upper case (POSTGRES ).
SSL -encrypted. To continue after
N , send the usual StartupMessage and proceed without
encryption.
+ (Alternatively, it is permissible to issue a GSSENCRequest message
+ after an N response to try to
+ use
GSSAPI encryption instead
result from the server, until it returns no output. When sending the
results of gss_init_sec_context() to the server,
prepend the length of the message as a four byte integer in network byte
- order. If this is successful, then use gss_wrap() to
- encrypt the usual StartupMessage and all subsequent data, prepending the
- length of the result from gss_wrap() as a four byte
- integer in network byte order to the actual encrypted payload. Note that
- the server will only accept encrypted packets from the client which are less
- than 16kB; gss_wrap_size_limit() should be used by the
- client to determine the size of the unencrypted message which will fit
- within this limit and larger messages should be broken up into multiple
- gss_wrap() calls. Typical segments are 8kB of
- unencrypted data, resulting in encrypted packets of slightly larger than 8kB
- but well within the 16kB maximum. The server can be expected to not send
- encrypted packets of larger than 16kB to the client. To continue after
+ order.
+ To continue after
N , send the usual StartupMessage and proceed without
encryption.
+ (Alternatively, it is permissible to issue an SSLRequest message
+ after an N response to try to
+ use
SSL encryption instead
support to
PostgreSQL . In this case the
connection must be closed, but the frontend might choose to open a fresh
connection and proceed without requesting
GSSAPI
- encryption. Given the length limits specified above, the ErrorMessage can
- not be confused with a proper response from the server with an appropriate
- length.
+ encryption.
opened to send a CancelRequest message.
+ Once
GSSAPI encryption has been successfully
+ established, use gss_wrap() to
+ encrypt the usual StartupMessage and all subsequent data, prepending the
+ length of the result from gss_wrap() as a four byte
+ integer in network byte order to the actual encrypted payload. Note that
+ the server will only accept encrypted packets from the client which are less
+ than 16kB; gss_wrap_size_limit() should be used by the
+ client to determine the size of the unencrypted message which will fit
+ within this limit and larger messages should be broken up into multiple
+ gss_wrap() calls. Typical segments are 8kB of
+ unencrypted data, resulting in encrypted packets of slightly larger than 8kB
+ but well within the 16kB maximum. The server can be expected to not send
+ encrypted packets of larger than 16kB to the client.
+
+
While the protocol itself does not provide a way for the server to
force
GSSAPI encryption, the administrator can
PostgreSQL also has native support for
using
GSSAPI to encrypt client/server communications for
increased security. Support requires that a
GSSAPI
- implementation (such as MIT krb5 ) is installed on both client and server
+ implementation (such as MIT Kerberos ) is installed on both client and server
systems, and that support in
PostgreSQL is
enabled at build time (see ).
some or all connections.
+ When using
GSSAPI for encryption, it is common to
+ use
GSSAPI for authentication as well, since the
+ underlying mechanism will determine both client and server identities
+ (according to the
GSSAPI implementation) in any
+ case. But this is not required;
+ another
PostgreSQL authentication method
+ can be chosen to perform additional verification.
+
+
Other than configuration of the negotiation
behavior,
GSSAPI encryption requires no setup beyond
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
*/
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
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
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)
*/
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
#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)
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],
* 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)));
}
#include "libpq/pqformat.h"
#include "miscadmin.h"
#include "pgstat.h"
+#include "utils/memutils.h"
/*
* 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)
* 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;
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;
* 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)
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
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;
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
* 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
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;
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);
&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;
}
/*
- * 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;
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;
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;
*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)
/* 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)
/* 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
/* 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;
}
#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
{
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))
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);
}
/* 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
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));
#include
#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 */
#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