Commit b0b39f72 authored by Stephen Frost's avatar Stephen Frost

GSSAPI encryption support

On both the frontend and backend, prepare for GSSAPI encryption
support by moving common code for error handling into a separate file.
Fix a TODO for handling multiple status messages in the process.
Eliminate the OIDs, which have not been needed for some time.

Add frontend and backend encryption support functions.  Keep the
context initiation for authentication-only separate on both the
frontend and backend in order to avoid concerns about changing the
requested flags to include encryption support.

In postmaster, pull GSSAPI authorization checking into a shared
function.  Also share the initiator name between the encryption and
non-encryption codepaths.

For HBA, add "hostgssenc" and "hostnogssenc" entries that behave
similarly to their SSL counterparts.  "hostgssenc" requires either
"gss", "trust", or "reject" for its authentication.

Similarly, add a "gssencmode" parameter to libpq.  Supported values are
"disable", "require", and "prefer".  Notably, negotiation will only be
attempted if credentials can be acquired.  Move credential acquisition
into its own function to support this behavior.

Add a simple pg_stat_gssapi view similar to pg_stat_ssl, for monitoring
if GSSAPI authentication was used, what principal was used, and if
encryption is being used on the connection.

Finally, add documentation for everything new, and update existing
documentation on connection security.

Thanks to Michael Paquier for the Windows fixes.

Author: Robbie Harwood, with changes to the read/write functions by me.
Reviewed in various forms and at different times by: Michael Paquier,
   Andres Freund, David Steele.
Discussion: https://www.postgresql.org/message-id/flat/jlg1tgq1ktm.fsf@thriss.redhat.com
parent 5f6fc34a
......@@ -108,6 +108,8 @@ hostnossl <replaceable>database</replaceable> <replaceable>user</replaceable>
host <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>IP-address</replaceable> <replaceable>IP-mask</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-options</replaceable></optional>
hostssl <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>IP-address</replaceable> <replaceable>IP-mask</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-options</replaceable></optional>
hostnossl <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>IP-address</replaceable> <replaceable>IP-mask</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-options</replaceable></optional>
hostgssenc <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>IP-address</replaceable> <replaceable>IP-mask</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-options</replaceable></optional>
hostnogssenc <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>IP-address</replaceable> <replaceable>IP-mask</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-options</replaceable></optional>
</synopsis>
The meaning of the fields is as follows:
......@@ -128,9 +130,10 @@ hostnossl <replaceable>database</replaceable> <replaceable>user</replaceable>
<listitem>
<para>
This record matches connection attempts made using TCP/IP.
<literal>host</literal> records match either
<literal>host</literal> records match
<acronym>SSL</acronym> or non-<acronym>SSL</acronym> connection
attempts.
attempts as well as <acronym>GSSAPI</acronym> encrypted or
non-<acronym>GSSAPI</acronym> encrypted connection attempts.
</para>
<note>
<para>
......@@ -176,6 +179,42 @@ hostnossl <replaceable>database</replaceable> <replaceable>user</replaceable>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>hostgssenc</literal></term>
<listitem>
<para>
This record matches connection attempts made using TCP/IP,
but only when the connection is made with <acronym>GSSAPI</acronym>
encryption.
</para>
<para>
To make use of this option the server must be built with
<acronym>GSSAPI</acronym> support. Otherwise,
the <literal>hostgssenc</literal> record is ignored except for logging
a warning that it cannot match any connections.
</para>
<para>
Note that the only supported <xref linkend="auth-methods"/> for use
with <acronym>GSSAPI</acronym> encryption
are <literal>gss</literal>, <literal>reject</literal>,
and <literal>trust</literal>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>hostnogssenc</literal></term>
<listitem>
<para>
This record type has the opposite behavior of <literal>hostgssenc</literal>;
it only matches connection attempts made over
TCP/IP that do not use <acronym>GSSAPI</acronym> encryption.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable>database</replaceable></term>
<listitem>
......@@ -450,8 +489,9 @@ hostnossl <replaceable>database</replaceable> <replaceable>user</replaceable>
<listitem>
<para>
Use GSSAPI to authenticate the user. This is only
available for TCP/IP connections. See <xref
linkend="gssapi-auth"/> for details.
available for TCP/IP connections . See <xref
linkend="gssapi-auth"/> for details. It can be used in conjunction
with GSSAPI encryption.
</para>
</listitem>
</varlistentry>
......@@ -703,15 +743,18 @@ host postgres all 192.168.12.10/32 scram-sha-256
host all mike .example.com md5
host all all .example.com scram-sha-256
# In the absence of preceding "host" lines, these two lines will
# In the absence of preceding "host" lines, these three lines will
# reject all connections from 192.168.54.1 (since that entry will be
# matched first), but allow GSSAPI connections from anywhere else
# on the Internet. The zero mask causes no bits of the host IP
# address to be considered, so it matches any host.
# matched first), but allow GSSAPI-encrypted connections from anywhere else
# on the Internet. The zero mask causes no bits of the host IP address to
# be considered, so it matches any host. Unencrypted GSSAPI connections
# (which "fall through" to the third line since "hostgssenc" only matches
# encrypted GSSAPI connections) are allowed, but only from 192.168.12.10.
#
# TYPE DATABASE USER ADDRESS METHOD
host all all 192.168.54.1/32 reject
host all all 0.0.0.0/0 gss
hostgssenc all all 0.0.0.0/0 gss
host all all 192.168.12.10/32 gss
# Allow users from 192.168.x.x hosts to connect to any database, if
# they pass the ident check. If, for example, ident says the user is
......@@ -1058,13 +1101,16 @@ omicron bryanh guest1
<para>
<productname>GSSAPI</productname> is an industry-standard protocol
for secure authentication defined in RFC 2743.
<productname>PostgreSQL</productname> supports
<productname>GSSAPI</productname> with <productname>Kerberos</productname>
authentication according to RFC 1964. <productname>GSSAPI</productname>
provides automatic authentication (single sign-on) for systems
that support it. The authentication itself is secure, but the
data sent over the database connection will be sent unencrypted unless
<acronym>SSL</acronym> is used.
<productname>PostgreSQL</productname>
supports <productname>GSSAPI</productname> for use as either an encrypted,
authenticated layer, or for authentication only.
<productname>GSSAPI</productname> provides automatic authentication
(single sign-on) for systems that support it. The authentication itself is
secure. If <productname>GSSAPI</productname> encryption
(see <literal>hostgssenc</literal>) or <acronym>SSL</acronym> encryption are
used, the data sent along the database connection will be encrypted;
otherwise, it will not.
</para>
<para>
......
......@@ -1316,6 +1316,63 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
</listitem>
</varlistentry>
<varlistentry id="libpq-connect-gssencmode" xreflabel="gssencmode">
<term><literal>gssencmode</literal></term>
<listitem>
<para>
This option determines whether or with what priority a secure
<acronym>GSS</acronym> TCP/IP connection will be negotiated with the
server. There are three modes:
<variablelist>
<varlistentry>
<term><literal>disable</literal></term>
<listitem>
<para>
only try a non-<acronym>GSSAPI</acronym>-encrypted connection
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>prefer</literal> (default)</term>
<listitem>
<para>
if there are <acronym>GSSAPI</acronym> credentials present (i.e.,
in a credentials cache), first try
a <acronym>GSSAPI</acronym>-encrypted connection; if that fails or
there are no credentials, try a
non-<acronym>GSSAPI</acronym>-encrypted connection. This is the
default when <productname>PostgreSQL</productname> has been
compiled with <acronym>GSSAPI</acronym> support.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>require</literal></term>
<listitem>
<para>
only try a <acronym>GSSAPI</acronym>-encrypted connection
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
<para>
<literal>gssencmode</literal> is ignored for Unix domain socket
communication. If <productname>PostgreSQL</productname> is compiled
without GSSAPI support, using the <literal>require</literal> option
will cause an error, while <literal>prefer</literal> will be accepted
but <application>libpq</application> will not actually attempt
a <acronym>GSSAPI</acronym>-encrypted
connection.<indexterm><primary>GSSAPI</primary><secondary sortas="libpq">with
libpq</secondary></indexterm>
</para>
</listitem>
</varlistentry>
<varlistentry id="libpq-connect-sslmode" xreflabel="sslmode">
<term><literal>sslmode</literal></term>
<listitem>
......@@ -7948,7 +8005,7 @@ ldap://ldap.acme.com/cn=dbserver,cn=hosts?pgconnectinfo?base?(objectclass=*)
</para>
<para>
For a connection to be known secure, SSL usage must be configured
For a connection to be known SSL-secured, SSL usage must be configured
on <emphasis>both the client and the server</emphasis> before the connection
is made. If it is only configured on the server, the client may end up
sending sensitive information (e.g. passwords) before
......
......@@ -336,6 +336,14 @@ postgres 27093 0.0 0.0 30096 2752 ? Ss 11:34 0:00 postgres: ser
</entry>
</row>
<row>
<entry><structname>pg_stat_gssapi</structname><indexterm><primary>pg_stat_gssapi</primary></indexterm></entry>
<entry>One row per connection (regular and replication), showing information about
GSSAPI authentication and encryption used on this connection.
See <xref linkend="pg-stat-gssapi-view"/> for details.
</entry>
</row>
<row>
<entry><structname>pg_stat_progress_create_index</structname><indexterm><primary>pg_stat_progress_create_index</primary></indexterm></entry>
<entry>One row for each backend running <command>CREATE INDEX</command>, showing
......@@ -2281,6 +2289,55 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
connection.
</para>
<table id="pg-stat-gssapi-view" xreflabel="pg_stat_gssapi">
<title><structname>pg_stat_gssapi</structname> View</title>
<tgroup cols="3">
<thead>
<row>
<entry>Column</entry>
<entry>Type</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry><structfield>pid</structfield></entry>
<entry><type>integer</type></entry>
<entry>Process ID of a backend</entry>
</row>
<row>
<entry><structfield>gss_authenticated</structfield></entry>
<entry><type>boolean</type></entry>
<entry>True if GSSAPI authentication was used for this connection</entry>
</row>
<row>
<entry><structfield>principal</structfield></entry>
<entry><type>text</type></entry>
<entry>Principal used to authenticate this connection, or NULL
if GSSAPI was not used to authenticate this connection. This
field is truncated if the principal is longer than
<symbol>NAMEDATALEN</symbol> (64 characters in a standard build).
</entry>
</row>
<row>
<entry><structfield>encrypted</structfield></entry>
<entry><type>boolean</type></entry>
<entry>True if GSSAPI encryption is in use on this connection</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
The <structname>pg_stat_gssapi</structname> view will contain one row per
backend, showing information about GSSAPI usage on this connection. It can
be joined to <structname>pg_stat_activity</structname> or
<structname>pg_stat_replication</structname> on the
<structfield>pid</structfield> column to get more details about the
connection.
</para>
<table id="pg-stat-archiver-view" xreflabel="pg_stat_archiver">
<title><structname>pg_stat_archiver</structname> View</title>
......
......@@ -2037,9 +2037,13 @@ pg_dumpall -p 5432 | psql -d postgres -p 5433
</para>
<para>
To prevent spoofing on TCP connections, the best solution is to use
SSL certificates and make sure that clients check the server's certificate.
To do that, the server
To prevent spoofing on TCP connections, either use
SSL certificates and make sure that clients check the server's certificate,
or use GSSAPI encryption (or both, if they're on separate connections).
</para>
<para>
To prevent spoofing with SSL, the server
must be configured to accept only <literal>hostssl</literal> connections (<xref
linkend="auth-pg-hba-conf"/>) and have SSL key and certificate files
(<xref linkend="ssl-tcp"/>). The TCP client must connect using
......@@ -2047,6 +2051,14 @@ pg_dumpall -p 5432 | psql -d postgres -p 5433
<literal>verify-full</literal> and have the appropriate root certificate
file installed (<xref linkend="libq-ssl-certificates"/>).
</para>
<para>
To prevent spoofing with GSSAPI, the server must be configured to accept
only <literal>hostgssenc</literal> connections
(<xref linkend="auth-pg-hba-conf"/>) and use <literal>gss</literal>
authentication with them. The TCP client must connect
using <literal>gssencmode=require</literal>.
</para>
</sect1>
<sect1 id="encryption-options">
......@@ -2143,8 +2155,24 @@ pg_dumpall -p 5432 | psql -d postgres -p 5433
which hosts can use non-encrypted connections (<literal>host</literal>)
and which require SSL-encrypted connections
(<literal>hostssl</literal>). Also, clients can specify that they
connect to servers only via SSL. <application>Stunnel</application> or
<application>SSH</application> can also be used to encrypt transmissions.
connect to servers only via SSL.
</para>
<para>
GSSAPI-encrypted connections encrypt all data sent across the network,
including queries and data returned. (No password is sent across the
network.) The <filename>pg_hba.conf</filename> file allows
administrators to specify which hosts can use non-encrypted connections
(<literal>host</literal>) and which require GSSAPI-encrypted connections
(<literal>hostgssenc</literal>). Also, clients can specify that they
connect to servers only on GSSAPI-encrypted connections
(<literal>gssencmode=require</literal>).
</para>
<para>
<application>Stunnel</application> or
<application>SSH</application> can also be used to encrypt
transmissions.
</para>
</listitem>
</varlistentry>
......@@ -2561,6 +2589,45 @@ openssl x509 -req -in server.csr -text -days 365 \
</sect1>
<sect1 id="gssapi-enc">
<title>Secure TCP/IP Connections with GSSAPI encryption</title>
<indexterm zone="gssapi-enc">
<primary>gssapi</primary>
</indexterm>
<para>
<productname>PostgreSQL</productname> also has native support for
using <acronym>GSSAPI</acronym> to encrypt client/server communications for
increased security. Support requires that a <acronym>GSSAPI</acronym>
implementation (such as MIT krb5) is installed on both client and server
systems, and that support in <productname>PostgreSQL</productname> is
enabled at build time (see <xref linkend="installation"/>).
</para>
<sect2 id="gssapi-setup">
<title>Basic Setup</title>
<para>
The <productname>PostgreSQL</productname> server will listen for both
normal and <acronym>GSSAPI</acronym>-encrypted connections on the same TCP
port, and will negotiate with any connecting client on whether to
use <acronym>GSSAPI</acronym> for encryption (and for authentication). By
default, this decision is up to the client (which means it can be
downgraded by an attacker); see <xref linkend="auth-pg-hba-conf"/> about
setting up the server to require the use of <acronym>GSSAPI</acronym> for
some or all conections.
</para>
<para>
Other than configuration of the negotiation
behavior, <acronym>GSSAPI</acronym> encryption requires no setup beyond
that which is necessary for GSSAPI authentication. (For more information
on configuring that, see <xref linkend="gssapi-auth"/>.)
</para>
</sect2>
</sect1>
<sect1 id="ssh-tunnels">
<title>Secure TCP/IP Connections with <application>SSH</application> Tunnels</title>
......
......@@ -787,6 +787,14 @@ CREATE VIEW pg_stat_ssl AS
S.ssl_issuer_dn AS issuer_dn
FROM pg_stat_get_activity(NULL) AS S;
CREATE VIEW pg_stat_gssapi AS
SELECT
S.pid,
S.gss_auth AS gss_authenticated,
S.gss_princ AS principal,
S.gss_enc AS encrypted
FROM pg_stat_get_activity(NULL) AS S;
CREATE VIEW pg_replication_slots AS
SELECT
L.slot_name,
......
......@@ -21,4 +21,8 @@ ifeq ($(with_openssl),yes)
OBJS += be-secure-openssl.o
endif
ifeq ($(with_gssapi),yes)
OBJS += be-gssapi-common.o be-secure-gssapi.o
endif
include $(top_srcdir)/src/backend/common.mk
......@@ -36,6 +36,7 @@
#include "port/pg_bswap.h"
#include "replication/walsender.h"
#include "storage/ipc.h"
#include "utils/memutils.h"
#include "utils/timestamp.h"
......@@ -172,12 +173,9 @@ bool pg_krb_caseins_users;
*----------------------------------------------------------------
*/
#ifdef ENABLE_GSS
#if defined(HAVE_GSSAPI_H)
#include <gssapi.h>
#else
#include <gssapi/gssapi.h>
#endif
#include "be-gssapi-common.h"
static int pg_GSS_checkauth(Port *port);
static int pg_GSS_recvauth(Port *port);
#endif /* ENABLE_GSS */
......@@ -383,6 +381,17 @@ 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
*/
......@@ -523,8 +532,14 @@ ClientAuthentication(Port *port)
case uaGSS:
#ifdef ENABLE_GSS
sendAuthRequest(port, AUTH_REQ_GSS, NULL, 0);
status = pg_GSS_recvauth(port);
port->gss->auth = true;
if (port->gss->enc)
status = pg_GSS_checkauth(port);
else
{
sendAuthRequest(port, AUTH_REQ_GSS, NULL, 0);
status = pg_GSS_recvauth(port);
}
#else
Assert(false);
#endif
......@@ -1031,68 +1046,6 @@ CheckSCRAMAuth(Port *port, char *shadow_pass, char **logdetail)
*----------------------------------------------------------------
*/
#ifdef ENABLE_GSS
#if defined(WIN32) && !defined(_MSC_VER)
/*
* MIT Kerberos GSSAPI DLL doesn't properly export the symbols for MingW
* that contain the OIDs required. Redefine here, values copied
* from src/athena/auth/krb5/src/lib/gssapi/generic/gssapi_generic.c
*/
static const gss_OID_desc GSS_C_NT_USER_NAME_desc =
{10, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x02"};
static GSS_DLLIMP gss_OID GSS_C_NT_USER_NAME = &GSS_C_NT_USER_NAME_desc;
#endif
/*
* Generate an error for GSSAPI authentication. The caller should apply
* _() to errmsg to make it translatable.
*/
static void
pg_GSS_error(int severity, const char *errmsg, OM_uint32 maj_stat, OM_uint32 min_stat)
{
gss_buffer_desc gmsg;
OM_uint32 lmin_s,
msg_ctx;
char msg_major[128],
msg_minor[128];
/* Fetch major status message */
msg_ctx = 0;
gss_display_status(&lmin_s, maj_stat, GSS_C_GSS_CODE,
GSS_C_NO_OID, &msg_ctx, &gmsg);
strlcpy(msg_major, gmsg.value, sizeof(msg_major));
gss_release_buffer(&lmin_s, &gmsg);
if (msg_ctx)
/*
* More than one message available. XXX: Should we loop and read all
* messages? (same below)
*/
ereport(WARNING,
(errmsg_internal("incomplete GSS error report")));
/* Fetch mechanism minor status message */
msg_ctx = 0;
gss_display_status(&lmin_s, min_stat, GSS_C_MECH_CODE,
GSS_C_NO_OID, &msg_ctx, &gmsg);
strlcpy(msg_minor, gmsg.value, sizeof(msg_minor));
gss_release_buffer(&lmin_s, &gmsg);
if (msg_ctx)
ereport(WARNING,
(errmsg_internal("incomplete GSS minor error report")));
/*
* errmsg_internal, since translation of the first part must be done
* before calling this function anyway.
*/
ereport(severity,
(errmsg_internal("%s", errmsg),
errdetail_internal("%s: %s", msg_major, msg_minor)));
}
static int
pg_GSS_recvauth(Port *port)
{
......@@ -1101,7 +1054,6 @@ pg_GSS_recvauth(Port *port)
lmin_s,
gflags;
int mtype;
int ret;
StringInfoData buf;
gss_buffer_desc gbuf;
......@@ -1254,10 +1206,23 @@ pg_GSS_recvauth(Port *port)
*/
gss_release_cred(&min_stat, &port->gss->cred);
}
return pg_GSS_checkauth(port);
}
/*
* Check whether the GSSAPI-authenticated user is allowed to connect as the
* claimed username.
*/
static int
pg_GSS_checkauth(Port *port)
{
int ret;
OM_uint32 maj_stat,
min_stat,
lmin_s;
gss_buffer_desc gbuf;
/*
* GSS_S_COMPLETE indicates that authentication is now complete.
*
* Get the name of the user that authenticated, and compare it to the pg
* username that was specified for the connection.
*/
......@@ -1267,6 +1232,12 @@ pg_GSS_recvauth(Port *port)
_("retrieving GSS user name failed"),
maj_stat, min_stat);
/*
* Copy the original name of the authenticated principal into our backend
* memory for display later.
*/
port->gss->princ = MemoryContextStrdup(TopMemoryContext, gbuf.value);
/*
* Split the username at the realm separator
*/
......
/*-------------------------------------------------------------------------
*
* be-gssapi-common.c
* Common code for GSSAPI authentication and encryption
*
* Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* src/backend/libpq/be-gssapi-common.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "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.
*/
static void
pg_GSS_error_int(char *s, size_t len, OM_uint32 stat, int type)
{
gss_buffer_desc gmsg;
size_t i = 0;
OM_uint32 lmin_s,
msg_ctx = 0;
gmsg.value = NULL;
gmsg.length = 0;
do
{
gss_display_status(&lmin_s, stat, type,
GSS_C_NO_OID, &msg_ctx, &gmsg);
strlcpy(s + i, gmsg.value, len - i);
i += gmsg.length;
gss_release_buffer(&lmin_s, &gmsg);
}
while (msg_ctx && i < len);
if (msg_ctx || i == len)
ereport(WARNING,
(errmsg_internal("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.
*/
void
pg_GSS_error(int severity, const char *errmsg,
OM_uint32 maj_stat, OM_uint32 min_stat)
{
char msg_major[128],
msg_minor[128];
/* Fetch major status message */
pg_GSS_error_int(msg_major, sizeof(msg_major), maj_stat, GSS_C_GSS_CODE);
/* Fetch mechanism minor status message */
pg_GSS_error_int(msg_minor, sizeof(msg_minor), min_stat, GSS_C_MECH_CODE);
/*
* errmsg_internal, since translation of the first part must be done
* before calling this function anyway.
*/
ereport(severity,
(errmsg_internal("%s", errmsg),
errdetail_internal("%s: %s", msg_major, msg_minor)));
}
/*-------------------------------------------------------------------------
*
* be-gssapi-common.h
* Definitions for GSSAPI authentication and encryption handling
*
* Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* src/include/libpq/be-gssapi-common.h
*
*-------------------------------------------------------------------------
*/
#ifndef BE_GSSAPI_COMMON_H
#define BE_GSSAPI_COMMON_H
#if defined(HAVE_GSSAPI_H)
#include <gssapi.h>
#else
#include <gssapi/gssapi.h>
#endif
void pg_GSS_error(int severity, const char *errmsg,
OM_uint32 maj_stat, OM_uint32 min_stat);
#endif /* BE_GSSAPI_COMMON_H */
This diff is collapsed.
......@@ -159,6 +159,14 @@ retry:
n = be_tls_read(port, ptr, len, &waitfor);
}
else
#endif
#ifdef ENABLE_GSS
if (port->gss->enc)
{
n = be_gssapi_read(port, ptr, len);
waitfor = WL_SOCKET_READABLE;
}
else
#endif
{
n = secure_raw_read(port, ptr, len);
......@@ -264,6 +272,14 @@ retry:
n = be_tls_write(port, ptr, len, &waitfor);
}
else
#endif
#ifdef ENABLE_GSS
if (port->gss->enc)
{
n = be_gssapi_write(port, ptr, len);
waitfor = WL_SOCKET_WRITEABLE;
}
else
#endif
{
n = secure_raw_write(port, ptr, len);
......
......@@ -994,7 +994,9 @@ parse_hba_line(TokenizedLine *tok_line, int elevel)
}
else if (strcmp(token->string, "host") == 0 ||
strcmp(token->string, "hostssl") == 0 ||
strcmp(token->string, "hostnossl") == 0)
strcmp(token->string, "hostnossl") == 0 ||
strcmp(token->string, "hostgssenc") == 0 ||
strcmp(token->string, "hostnogssenc") == 0)
{
if (token->string[4] == 's') /* "hostssl" */
......@@ -1022,10 +1024,23 @@ parse_hba_line(TokenizedLine *tok_line, int elevel)
*err_msg = "hostssl record cannot match because SSL is not supported by this build";
#endif
}
else if (token->string[4] == 'n') /* "hostnossl" */
else if (token->string[4] == 'g') /* "hostgssenc" */
{
parsedline->conntype = ctHostNoSSL;
parsedline->conntype = ctHostGSS;
#ifndef ENABLE_GSS
ereport(elevel,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("hostgssenc record cannot match because GSSAPI is not supported by this build"),
errhint("Compile with --with-gssapi to use GSSAPI connections."),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
*err_msg = "hostgssenc record cannot match because GSSAPI is not supported by this build";
#endif
}
else if (token->string[4] == 'n' && token->string[6] == 's')
parsedline->conntype = ctHostNoSSL;
else if (token->string[4] == 'n' && token->string[6] == 'g')
parsedline->conntype = ctHostNoGSS;
else
{
/* "host" */
......@@ -1404,6 +1419,19 @@ 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 authenticaion";
return NULL;
}
if (parsedline->conntype != ctLocal &&
parsedline->auth_method == uaPeer)
......@@ -2078,6 +2106,17 @@ check_hba(hbaPort *port)
continue;
}
/* Check GSSAPI state */
#ifdef ENABLE_GSS
if (port->gss->enc && hba->conntype == ctHostNoGSS)
continue;
else if (!port->gss->enc && hba->conntype == ctHostGSS)
continue;
#else
if (hba->conntype == ctHostGSS)
continue;
#endif
/* Check IP address */
switch (hba->ip_cmp_method)
{
......@@ -2414,6 +2453,12 @@ fill_hba_line(Tuplestorestate *tuple_store, TupleDesc tupdesc,
case ctHostNoSSL:
typestr = "hostnossl";
break;
case ctHostGSS:
typestr = "hostgssenc";
break;
case ctHostNoGSS:
typestr = "hostnogssenc";
break;
}
if (typestr)
values[index++] = CStringGetTextDatum(typestr);
......
......@@ -2634,6 +2634,9 @@ static Size BackendActivityBufferSize = 0;
#ifdef USE_SSL
static PgBackendSSLStatus *BackendSslStatusBuffer = NULL;
#endif
#ifdef ENABLE_GSS
static PgBackendGSSStatus *BackendGssStatusBuffer = NULL;
#endif
/*
......@@ -2766,6 +2769,28 @@ CreateSharedBackendStatus(void)
}
}
#endif
#ifdef ENABLE_GSS
/* Create or attach to the shared GSSAPI status buffer */
size = mul_size(sizeof(PgBackendGSSStatus), NumBackendStatSlots);
BackendGssStatusBuffer = (PgBackendGSSStatus *)
ShmemInitStruct("Backend GSS Status Buffer", size, &found);
if (!found)
{
PgBackendGSSStatus *ptr;
MemSet(BackendGssStatusBuffer, 0, size);
/* Initialize st_gssstatus pointers. */
ptr = BackendGssStatusBuffer;
for (i = 0; i < NumBackendStatSlots; i++)
{
BackendStatusArray[i].st_gssstatus = ptr;
ptr++;
}
}
#endif
}
......@@ -2953,6 +2978,24 @@ pgstat_bestart(void)
#else
beentry->st_ssl = false;
#endif
#ifdef ENABLE_GSS
if (MyProcPort && MyProcPort->gss != NULL)
{
beentry->st_gss = true;
beentry->st_gssstatus->gss_auth = be_gssapi_get_auth(MyProcPort);
beentry->st_gssstatus->gss_enc = be_gssapi_get_enc(MyProcPort);
if (beentry->st_gssstatus->gss_auth)
strlcpy(beentry->st_gssstatus->gss_princ, be_gssapi_get_princ(MyProcPort), NAMEDATALEN);
}
else
{
beentry->st_gss = false;
}
#else
beentry->st_gss = false;
#endif
beentry->st_state = STATE_UNDEFINED;
beentry->st_appname[0] = '\0';
beentry->st_activity_raw[0] = '\0';
......@@ -3595,6 +3638,9 @@ pgstat_get_wait_client(WaitEventClient w)
case WAIT_EVENT_WAL_SENDER_WRITE_DATA:
event_name = "WalSenderWriteData";
break;
case WAIT_EVENT_GSS_OPEN_SERVER:
event_name = "GSSOpenServer";
break;
/* no default case, so that compiler will warn */
}
......
......@@ -1889,9 +1889,12 @@ initMasks(fd_set *rmask)
* if that's what you want. Return STATUS_ERROR if you don't want to
* send anything to the client, which would typically be appropriate
* if we detect a communications failure.)
*
* Set secure_done when negotiation of an encrypted layer (currently, TLS or
* GSSAPI) is already completed.
*/
static int
ProcessStartupPacket(Port *port, bool SSLdone)
ProcessStartupPacket(Port *port, bool secure_done)
{
int32 len;
void *buf;
......@@ -1924,9 +1927,10 @@ ProcessStartupPacket(Port *port, bool SSLdone)
if (pq_getbytes(((char *) &len) + 1, 3) == EOF)
{
/* Got a partial length word, so bleat about that */
ereport(COMMERROR,
(errcode(ERRCODE_PROTOCOL_VIOLATION),
errmsg("incomplete startup packet")));
if (!secure_done)
ereport(COMMERROR,
(errcode(ERRCODE_PROTOCOL_VIOLATION),
errmsg("incomplete startup packet")));
return STATUS_ERROR;
}
......@@ -1975,7 +1979,7 @@ ProcessStartupPacket(Port *port, bool SSLdone)
return STATUS_ERROR;
}
if (proto == NEGOTIATE_SSL_CODE && !SSLdone)
if (proto == NEGOTIATE_SSL_CODE && !secure_done)
{
char SSLok;
......@@ -2008,6 +2012,32 @@ retry1:
/* but not another SSL negotiation request */
return ProcessStartupPacket(port, true);
}
else if (proto == NEGOTIATE_GSS_CODE && !secure_done)
{
char GSSok = 'N';
#ifdef ENABLE_GSS
/* No GSSAPI encryption when on Unix socket */
if (!IS_AF_UNIX(port->laddr.addr.ss_family))
GSSok = 'G';
#endif
while (send(port->sock, &GSSok, 1, 0) != 1)
{
if (errno == EINTR)
continue;
ereport(COMMERROR,
(errcode_for_socket_access(),
errmsg("failed to send GSSAPI negotiation response: %m)")));
return STATUS_ERROR; /* close the connection */
}
#ifdef ENABLE_GSS
if (GSSok == 'G' && secure_open_gssapi(port) == -1)
return STATUS_ERROR;
#endif
/* Won't ever see more than one negotiation request */
return ProcessStartupPacket(port, true);
}
/* Could add additional special packet types here */
......
......@@ -545,7 +545,7 @@ pg_stat_get_progress_info(PG_FUNCTION_ARGS)
Datum
pg_stat_get_activity(PG_FUNCTION_ARGS)
{
#define PG_STAT_GET_ACTIVITY_COLS 26
#define PG_STAT_GET_ACTIVITY_COLS 29
int num_backends = pgstat_fetch_stat_numbackends();
int curr_backend;
int pid = PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0);
......@@ -859,6 +859,21 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
values[18] = BoolGetDatum(false); /* ssl */
nulls[19] = nulls[20] = nulls[21] = nulls[22] = nulls[23] = nulls[24] = nulls[25] = true;
}
/* GSSAPI information */
if (beentry->st_gss)
{
values[26] = BoolGetDatum(beentry->st_gssstatus->gss_auth); /* gss_auth */
values[27] = CStringGetTextDatum(beentry->st_gssstatus->gss_princ);
values[28] = BoolGetDatum(beentry->st_gssstatus->gss_enc); /* GSS Encryption in use */
}
else
{
values[26] = BoolGetDatum(false); /* gss_auth */
nulls[27] = true; /* No GSS principal */
values[28] = BoolGetDatum(false); /* GSS Encryption not in
* use */
}
}
else
{
......@@ -883,6 +898,9 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
nulls[23] = true;
nulls[24] = true;
nulls[25] = true;
nulls[26] = true;
nulls[27] = true;
nulls[28] = true;
}
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
......
......@@ -160,6 +160,7 @@ static void print_with_linenumbers(FILE *output, char *lines,
static void minimal_error_message(PGresult *res);
static void printSSLInfo(void);
static void printGSSInfo(void);
static bool printPsetInfo(const char *param, struct printQueryOpt *popt);
static char *pset_value_string(const char *param, struct printQueryOpt *popt);
......@@ -621,6 +622,7 @@ exec_command_conninfo(PsqlScanState scan_state, bool active_branch)
db, PQuser(pset.db), host, PQport(pset.db));
}
printSSLInfo();
printGSSInfo();
}
}
......@@ -3184,6 +3186,7 @@ connection_warnings(bool in_startup)
checkWin32Codepage();
#endif
printSSLInfo();
printGSSInfo();
}
}
......@@ -3216,6 +3219,20 @@ printSSLInfo(void)
(compression && strcmp(compression, "off") != 0) ? _("on") : _("off"));
}
/*
* printGSSInfo
*
* Prints information about the current GSSAPI connection, if GSSAPI encryption is in use
*/
static void
printGSSInfo(void)
{
if (!PQgssEncInUse(pset.db))
return; /* no GSSAPI encryption in use */
printf(_("GSSAPI Encrypted connection\n"));
}
/*
* checkWin32Codepage
......
......@@ -5117,9 +5117,9 @@
proname => 'pg_stat_get_activity', prorows => '100', proisstrict => 'f',
proretset => 't', provolatile => 's', proparallel => 'r',
prorettype => 'record', proargtypes => 'int4',
proallargtypes => '{int4,oid,int4,oid,text,text,text,text,text,timestamptz,timestamptz,timestamptz,timestamptz,inet,text,int4,xid,xid,text,bool,text,text,int4,bool,text,numeric,text}',
proargmodes => '{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}',
proargnames => '{pid,datid,pid,usesysid,application_name,state,query,wait_event_type,wait_event,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin,backend_type,ssl,sslversion,sslcipher,sslbits,sslcompression,ssl_client_dn,ssl_client_serial,ssl_issuer_dn}',
proallargtypes => '{int4,oid,int4,oid,text,text,text,text,text,timestamptz,timestamptz,timestamptz,timestamptz,inet,text,int4,xid,xid,text,bool,text,text,int4,bool,text,numeric,text,bool,text,bool}',
proargmodes => '{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}',
proargnames => '{pid,datid,pid,usesysid,application_name,state,query,wait_event_type,wait_event,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin,backend_type,ssl,sslversion,sslcipher,sslbits,sslcompression,ssl_client_dn,ssl_client_serial,ssl_issuer_dn,gss_auth,gss_princ,gss_enc}',
prosrc => 'pg_stat_get_activity' },
{ oid => '3318',
descr => 'statistics: information about progress of backends running maintenance command',
......
......@@ -55,7 +55,9 @@ typedef enum ConnType
ctLocal,
ctHost,
ctHostSSL,
ctHostNoSSL
ctHostNoSSL,
ctHostGSS,
ctHostNoGSS,
} ConnType;
typedef enum ClientCertMode
......
......@@ -86,6 +86,10 @@ typedef struct
gss_cred_id_t cred; /* GSSAPI connection cred's */
gss_ctx_id_t ctx; /* GSSAPI connection context */
gss_name_t name; /* GSSAPI client name */
char *princ; /* GSSAPI Principal used for auth, NULL if
* GSSAPI auth was not used */
bool auth; /* GSSAPI Authentication used */
bool enc; /* GSSAPI encryption in use */
#endif
} pg_gssinfo;
#endif
......@@ -164,6 +168,9 @@ typedef struct Port
int keepalives_interval;
int keepalives_count;
/*
* GSSAPI structures.
*/
#if defined(ENABLE_GSS) || defined(ENABLE_SSPI)
/*
......@@ -262,6 +269,13 @@ extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);
/*
* Return information about the GSSAPI authenticated connection
*/
extern bool be_gssapi_get_auth(Port *port);
extern bool be_gssapi_get_enc(Port *port);
extern const char *be_gssapi_get_princ(Port *port);
/*
* Get the server certificate hash for SCRAM channel binding type
* tls-server-end-point.
......@@ -279,6 +293,12 @@ extern char *be_tls_get_certificate_hash(Port *port, size_t *len);
#endif /* USE_SSL */
#ifdef ENABLE_GSS
/* Read and write to a GSSAPI-encrypted connection. */
extern ssize_t be_gssapi_read(Port *port, void *ptr, size_t len);
extern ssize_t be_gssapi_write(Port *port, void *ptr, size_t len);
#endif /* ENABLE_GSS */
extern ProtocolVersion FrontendProtocol;
/* TCP keepalives configuration. These are no-ops on an AF_UNIX socket. */
......
......@@ -93,6 +93,9 @@ extern ssize_t secure_read(Port *port, void *ptr, size_t len);
extern ssize_t secure_write(Port *port, void *ptr, size_t len);
extern ssize_t secure_raw_read(Port *port, void *ptr, size_t len);
extern ssize_t secure_raw_write(Port *port, const void *ptr, size_t len);
#ifdef ENABLE_GSS
extern ssize_t secure_open_gssapi(Port *port);
#endif
extern bool ssl_loaded_verify_locations;
......
......@@ -199,9 +199,10 @@ typedef struct CancelRequestPacket
/*
* A client can also start by sending a SSL negotiation request, to get a
* secure channel.
* A client can also start by sending a SSL or GSSAPI negotiation request to
* get a secure channel.
*/
#define NEGOTIATE_SSL_CODE PG_PROTOCOL(1234,5679)
#define NEGOTIATE_GSS_CODE PG_PROTOCOL(1234,5680)
#endif /* PQCOMM_H */
......@@ -801,7 +801,8 @@ typedef enum
WAIT_EVENT_SSL_OPEN_SERVER,
WAIT_EVENT_WAL_RECEIVER_WAIT_START,
WAIT_EVENT_WAL_SENDER_WAIT_WAL,
WAIT_EVENT_WAL_SENDER_WRITE_DATA
WAIT_EVENT_WAL_SENDER_WRITE_DATA,
WAIT_EVENT_GSS_OPEN_SERVER,
} WaitEventClient;
/* ----------
......@@ -989,6 +990,23 @@ typedef struct PgBackendSSLStatus
char ssl_issuer_dn[NAMEDATALEN];
} PgBackendSSLStatus;
/*
* PgBackendGSSStatus
*
* For each backend, we keep the GSS status in a separate struct, that
* is only filled in if GSS is enabled.
*
* All char arrays must be null-terminated.
*/
typedef struct PgBackendGSSStatus
{
/* Information about GSSAPI connection */
char gss_princ[NAMEDATALEN]; /* GSSAPI Principal used to auth */
bool gss_auth; /* If GSSAPI authentication was used */
bool gss_enc; /* If encryption is being used */
} PgBackendGSSStatus;
/* ----------
* PgBackendStatus
......@@ -1043,6 +1061,10 @@ typedef struct PgBackendStatus
bool st_ssl;
PgBackendSSLStatus *st_sslstatus;
/* Information about GSSAPI connection */
bool st_gss;
PgBackendGSSStatus *st_gssstatus;
/* current state */
BackendState st_state;
......
......@@ -38,6 +38,10 @@ ifeq ($(with_openssl),yes)
OBJS += fe-secure-openssl.o fe-secure-common.o
endif
ifeq ($(with_gssapi),yes)
OBJS += fe-gssapi-common.o fe-secure-gssapi.o
endif
ifeq ($(PORTNAME), cygwin)
override shlib = cyg$(NAME)$(DLSUFFIX)
endif
......
......@@ -174,3 +174,5 @@ PQresultVerboseErrorMessage 171
PQencryptPasswordConn 172
PQresultMemorySize 173
PQhostaddr 174
PQgssEncInUse 175
PQgetgssctx 176
......@@ -49,52 +49,7 @@
* GSSAPI authentication system.
*/
#if defined(WIN32) && !defined(_MSC_VER)
/*
* MIT Kerberos GSSAPI DLL doesn't properly export the symbols for MingW
* that contain the OIDs required. Redefine here, values copied
* from src/athena/auth/krb5/src/lib/gssapi/generic/gssapi_generic.c
*/
static const gss_OID_desc GSS_C_NT_HOSTBASED_SERVICE_desc =
{10, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04"};
static GSS_DLLIMP gss_OID GSS_C_NT_HOSTBASED_SERVICE = &GSS_C_NT_HOSTBASED_SERVICE_desc;
#endif
/*
* Fetch all errors of a specific type and append to "str".
*/
static void
pg_GSS_error_int(PQExpBuffer str, const char *mprefix,
OM_uint32 stat, int type)
{
OM_uint32 lmin_s;
gss_buffer_desc lmsg;
OM_uint32 msg_ctx = 0;
do
{
gss_display_status(&lmin_s, stat, type,
GSS_C_NO_OID, &msg_ctx, &lmsg);
appendPQExpBuffer(str, "%s: %s\n", mprefix, (char *) lmsg.value);
gss_release_buffer(&lmin_s, &lmsg);
} while (msg_ctx);
}
/*
* GSSAPI errors contain two parts; put both into conn->errorMessage.
*/
static void
pg_GSS_error(const char *mprefix, PGconn *conn,
OM_uint32 maj_stat, OM_uint32 min_stat)
{
resetPQExpBuffer(&conn->errorMessage);
/* Fetch major error codes */
pg_GSS_error_int(&conn->errorMessage, mprefix, maj_stat, GSS_C_GSS_CODE);
/* Add the minor codes as well */
pg_GSS_error_int(&conn->errorMessage, mprefix, min_stat, GSS_C_MECH_CODE);
}
#include "fe-gssapi-common.h"
/*
* Continue GSS authentication with next token as needed.
......@@ -195,10 +150,7 @@ pg_GSS_continue(PGconn *conn, int payloadlen)
static int
pg_GSS_startup(PGconn *conn, int payloadlen)
{
OM_uint32 maj_stat,
min_stat;
int maxlen;
gss_buffer_desc temp_gbuf;
int ret;
char *host = conn->connhost[conn->whichhost].host;
if (!(host && host[0] != '\0'))
......@@ -215,33 +167,9 @@ pg_GSS_startup(PGconn *conn, int payloadlen)
return STATUS_ERROR;
}
/*
* Import service principal name so the proper ticket can be acquired by
* the GSSAPI system.
*/
maxlen = NI_MAXHOST + strlen(conn->krbsrvname) + 2;
temp_gbuf.value = (char *) malloc(maxlen);
if (!temp_gbuf.value)
{
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("out of memory\n"));
return STATUS_ERROR;
}
snprintf(temp_gbuf.value, maxlen, "%s@%s",
conn->krbsrvname, host);
temp_gbuf.length = strlen(temp_gbuf.value);
maj_stat = gss_import_name(&min_stat, &temp_gbuf,
GSS_C_NT_HOSTBASED_SERVICE, &conn->gtarg_nam);
free(temp_gbuf.value);
if (maj_stat != GSS_S_COMPLETE)
{
pg_GSS_error(libpq_gettext("GSSAPI name import error"),
conn,
maj_stat, min_stat);
return STATUS_ERROR;
}
ret = pg_GSS_load_servicename(conn);
if (ret != STATUS_OK)
return ret;
/*
* Initial packet is the same as a continuation packet with no initial
......@@ -977,7 +905,7 @@ pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn)
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("SSPI authentication not supported\n"));
return STATUS_ERROR;
#endif /* !define(ENABLE_GSSAPI) */
#endif /* !define(ENABLE_GSS) */
#endif /* ENABLE_SSPI */
......
......@@ -129,6 +129,12 @@ static int ldapServiceLookup(const char *purl, PQconninfoOption *options,
#else
#define DefaultSSLMode "disable"
#endif
#ifdef ENABLE_GSS
#include "fe-gssapi-common.h"
#define DefaultGSSMode "prefer"
#else
#define DefaultGSSMode "disable"
#endif
/* ----------
* Definition of the conninfo parameters and their fallback resources.
......@@ -298,6 +304,14 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
"Require-Peer", "", 10,
offsetof(struct pg_conn, requirepeer)},
/*
* Expose gssencmode similarly to sslmode - we can still handle "disable"
* and "prefer".
*/
{"gssencmode", "PGGSSMODE", DefaultGSSMode, NULL,
"GSS-Mode", "", 7, /* sizeof("disable") == 7 */
offsetof(struct pg_conn, gssencmode)},
#if defined(ENABLE_GSS) || defined(ENABLE_SSPI)
/* Kerberos and GSSAPI authentication support specifying the service name */
{"krbsrvname", "PGKRBSRVNAME", PG_KRB_SRVNAM, NULL,
......@@ -1226,6 +1240,39 @@ connectOptions2(PGconn *conn)
goto oom_error;
}
/*
* validate gssencmode option
*/
if (conn->gssencmode)
{
if (strcmp(conn->gssencmode, "disable") != 0 &&
strcmp(conn->gssencmode, "prefer") != 0 &&
strcmp(conn->gssencmode, "require") != 0)
{
conn->status = CONNECTION_BAD;
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("invalid gssencmode value: \"%s\"\n"),
conn->gssencmode);
return false;
}
#ifndef ENABLE_GSS
if (strcmp(conn->gssencmode, "require") == 0)
{
conn->status = CONNECTION_BAD;
printfPQExpBuffer(
&conn->errorMessage,
libpq_gettext("no GSSAPI support; cannot require GSSAPI\n"));
return false;
}
#endif
}
else
{
conn->gssencmode = strdup(DefaultGSSMode);
if (!conn->gssencmode)
goto oom_error;
}
/*
* Resolve special "auto" client_encoding from the locale
*/
......@@ -1827,6 +1874,11 @@ connectDBStart(PGconn *conn)
*/
resetPQExpBuffer(&conn->errorMessage);
#ifdef ENABLE_GSS
if (conn->gssencmode[0] == 'd') /* "disable" */
conn->try_gss = false;
#endif
/*
* Set up to try to connect to the first host. (Setting whichhost = -1 is
* a bit of a cheat, but PQconnectPoll will advance it to 0 before
......@@ -2099,6 +2151,7 @@ PQconnectPoll(PGconn *conn)
case CONNECTION_NEEDED:
case CONNECTION_CHECK_WRITABLE:
case CONNECTION_CONSUME:
case CONNECTION_GSS_STARTUP:
break;
default:
......@@ -2640,17 +2693,57 @@ keep_going: /* We will come back to here until there is
}
#endif /* HAVE_UNIX_SOCKETS */
if (IS_AF_UNIX(conn->raddr.addr.ss_family))
{
/* Don't request SSL or GSSAPI over Unix sockets */
#ifdef USE_SSL
conn->allow_ssl_try = false;
#endif
#ifdef ENABLE_GSS
conn->try_gss = false;
#endif
}
#ifdef ENABLE_GSS
/*
* If SSL is enabled and we haven't already got it running,
* request it instead of sending the startup message.
* If GSSAPI is enabled and we have a ccache, try to set it up
* before sending startup messages. If it's already
* operating, don't try SSL and instead just build the startup
* packet.
*/
if (IS_AF_UNIX(conn->raddr.addr.ss_family))
if (conn->try_gss && !conn->gctx)
conn->try_gss = pg_GSS_have_ccache(&conn->gcred);
if (conn->try_gss && !conn->gctx)
{
/* Don't bother requesting SSL over a Unix socket */
conn->allow_ssl_try = false;
ProtocolVersion pv = pg_hton32(NEGOTIATE_GSS_CODE);
if (pqPacketSend(conn, 0, &pv, sizeof(pv)) != STATUS_OK)
{
appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not send GSSAPI negotiation packet: %s\n"),
SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
goto error_return;
}
/* Ok, wait for response */
conn->status = CONNECTION_GSS_STARTUP;
return PGRES_POLLING_READING;
}
else if (!conn->gctx && conn->gssencmode[0] == 'r')
{
appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("GSSAPI encryption required, but was impossible (possibly no ccache, no server support, or using a local socket)\n"));
goto error_return;
}
#endif
#ifdef USE_SSL
/*
* If SSL is enabled and we haven't already got it running,
* request it instead of sending the startup message.
*/
if (conn->allow_ssl_try && !conn->wait_ssl_try &&
!conn->ssl_in_use)
{
......@@ -2844,6 +2937,98 @@ keep_going: /* We will come back to here until there is
#endif /* USE_SSL */
}
case CONNECTION_GSS_STARTUP:
{
#ifdef ENABLE_GSS
PostgresPollingStatusType pollres;
/*
* If we haven't yet, get the postmaster's response to our
* negotiation packet
*/
if (conn->try_gss && !conn->gctx)
{
char gss_ok;
int rdresult = pqReadData(conn);
if (rdresult < 0)
/* pqReadData fills in error message */
goto error_return;
else if (rdresult == 0)
/* caller failed to wait for data */
return PGRES_POLLING_READING;
if (pqGetc(&gss_ok, conn) < 0)
/* shouldn't happen... */
return PGRES_POLLING_READING;
if (gss_ok == 'E')
{
/*
* Server failure of some sort. Assume it's a
* protocol version support failure, and let's see if
* we can't recover (if it's not, we'll get a better
* error message on retry). Server gets fussy if we
* don't hang up the socket, though.
*/
conn->try_gss = false;
pqDropConnection(conn, true);
conn->status = CONNECTION_NEEDED;
goto keep_going;
}
/* mark byte consumed */
conn->inStart = conn->inCursor;
if (gss_ok == 'N')
{
/* Server doesn't want GSSAPI; fall back if we can */
if (conn->gssencmode[0] == 'r')
{
appendPQExpBufferStr(&conn->errorMessage,
libpq_gettext("server doesn't support GSSAPI encryption, but it was required\n"));
goto error_return;
}
conn->try_gss = false;
conn->status = CONNECTION_MADE;
return PGRES_POLLING_WRITING;
}
else if (gss_ok != 'G')
{
appendPQExpBuffer(&conn->errorMessage,
libpq_gettext("received invalid response to GSSAPI negotiation: %c\n"),
gss_ok);
goto error_return;
}
}
/* Begin or continue GSSAPI negotiation */
pollres = pqsecure_open_gss(conn);
if (pollres == PGRES_POLLING_OK)
{
/* All set for startup packet */
conn->status = CONNECTION_MADE;
return PGRES_POLLING_WRITING;
}
else if (pollres == PGRES_POLLING_FAILED &&
conn->gssencmode[0] == 'p')
{
/*
* We failed, but we can retry on "prefer". Have to drop
* the current connection to do so, though.
*/
conn->try_gss = false;
pqDropConnection(conn, true);
conn->status = CONNECTION_NEEDED;
goto keep_going;
}
return pollres;
#else /* !ENABLE_GSS */
/* unreachable */
goto error_return;
#endif /* ENABLE_GSS */
}
/*
* Handle authentication exchange: wait for postmaster messages
* and respond as necessary.
......@@ -2997,6 +3182,26 @@ keep_going: /* We will come back to here until there is
/* Check to see if we should mention pgpassfile */
pgpassfileWarning(conn);
#ifdef ENABLE_GSS
/*
* If gssencmode is "prefer" and we're using GSSAPI, retry
* without it.
*/
if (conn->gssenc && conn->gssencmode[0] == 'p')
{
OM_uint32 minor;
/* postmaster expects us to drop the connection */
conn->try_gss = false;
conn->gssenc = false;
gss_delete_sec_context(&minor, &conn->gctx, NULL);
pqDropConnection(conn, true);
conn->status = CONNECTION_NEEDED;
goto keep_going;
}
#endif
#ifdef USE_SSL
/*
......@@ -3564,6 +3769,9 @@ makeEmptyPGconn(void)
conn->verbosity = PQERRORS_DEFAULT;
conn->show_context = PQSHOW_CONTEXT_ERRORS;
conn->sock = PGINVALID_SOCKET;
#ifdef ENABLE_GSS
conn->try_gss = true;
#endif
/*
* We try to send at least 8K at a time, which is the usual size of pipe
......@@ -3695,10 +3903,28 @@ freePGconn(PGconn *conn)
free(conn->requirepeer);
if (conn->connip)
free(conn->connip);
if (conn->gssencmode)
free(conn->gssencmode);
#if defined(ENABLE_GSS) || defined(ENABLE_SSPI)
if (conn->krbsrvname)
free(conn->krbsrvname);
#endif
#ifdef ENABLE_GSS
if (conn->gcred != GSS_C_NO_CREDENTIAL)
{
OM_uint32 minor;
gss_release_cred(&minor, &conn->gcred);
conn->gcred = GSS_C_NO_CREDENTIAL;
}
if (conn->gctx)
{
OM_uint32 minor;
gss_delete_sec_context(&minor, &conn->gctx, GSS_C_NO_BUFFER);
conn->gctx = NULL;
}
#endif
#if defined(ENABLE_GSS) && defined(ENABLE_SSPI)
if (conn->gsslib)
free(conn->gsslib);
......
/*-------------------------------------------------------------------------
*
* fe-gssapi-common.c
* The front-end (client) GSSAPI common code
*
* Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* src/interfaces/libpq/fe-gssapi-common.c
*-------------------------------------------------------------------------
*/
#include "postgres_fe.h"
#include "fe-gssapi-common.h"
#include "libpq-int.h"
#include "pqexpbuffer.h"
/*
* Fetch all errors of a specific type and append to "str".
*/
static void
pg_GSS_error_int(PQExpBuffer str, const char *mprefix,
OM_uint32 stat, int type)
{
OM_uint32 lmin_s;
gss_buffer_desc lmsg;
OM_uint32 msg_ctx = 0;
do
{
gss_display_status(&lmin_s, stat, type,
GSS_C_NO_OID, &msg_ctx, &lmsg);
appendPQExpBuffer(str, "%s: %s\n", mprefix, (char *) lmsg.value);
gss_release_buffer(&lmin_s, &lmsg);
} while (msg_ctx);
}
/*
* GSSAPI errors contain two parts; put both into conn->errorMessage.
*/
void
pg_GSS_error(const char *mprefix, PGconn *conn,
OM_uint32 maj_stat, OM_uint32 min_stat)
{
resetPQExpBuffer(&conn->errorMessage);
/* Fetch major error codes */
pg_GSS_error_int(&conn->errorMessage, mprefix, maj_stat, GSS_C_GSS_CODE);
/* Add the minor codes as well */
pg_GSS_error_int(&conn->errorMessage, mprefix, min_stat, GSS_C_MECH_CODE);
}
/*
* Check if we can acquire credentials at all (and yield them if so).
*/
bool
pg_GSS_have_ccache(gss_cred_id_t *cred_out)
{
OM_uint32 major,
minor;
gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
major = gss_acquire_cred(&minor, GSS_C_NO_NAME, 0, GSS_C_NO_OID_SET,
GSS_C_INITIATE, &cred, NULL, NULL);
if (major != GSS_S_COMPLETE)
{
*cred_out = NULL;
return false;
}
*cred_out = cred;
return true;
}
/*
* Try to load service name for a connection
*/
int
pg_GSS_load_servicename(PGconn *conn)
{
OM_uint32 maj_stat,
min_stat;
int maxlen;
gss_buffer_desc temp_gbuf;
char *host;
if (conn->gtarg_nam != NULL)
/* Already taken care of - move along */
return STATUS_OK;
host = PQhost(conn);
if (!(host && host[0] != '\0'))
{
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("host name must be specified\n"));
return STATUS_ERROR;
}
/*
* Import service principal name so the proper ticket can be acquired by
* the GSSAPI system.
*/
maxlen = NI_MAXHOST + strlen(conn->krbsrvname) + 2;
temp_gbuf.value = (char *) malloc(maxlen);
if (!temp_gbuf.value)
{
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("out of memory\n"));
return STATUS_ERROR;
}
snprintf(temp_gbuf.value, maxlen, "%s@%s",
conn->krbsrvname, host);
temp_gbuf.length = strlen(temp_gbuf.value);
maj_stat = gss_import_name(&min_stat, &temp_gbuf,
GSS_C_NT_HOSTBASED_SERVICE, &conn->gtarg_nam);
free(temp_gbuf.value);
if (maj_stat != GSS_S_COMPLETE)
{
pg_GSS_error(libpq_gettext("GSSAPI name import error"),
conn,
maj_stat, min_stat);
return STATUS_ERROR;
}
return STATUS_OK;
}
/*-------------------------------------------------------------------------
*
* fe-gssapi-common.h
*
* Definitions for GSSAPI common routines
*
* Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* src/interfaces/libpq/fe-gssapi-common.h
*/
#ifndef FE_GSSAPI_COMMON_H
#define FE_GSSAPI_COMMON_H
#include "libpq-fe.h"
#include "libpq-int.h"
void pg_GSS_error(const char *mprefix, PGconn *conn,
OM_uint32 maj_stat, OM_uint32 min_stat);
bool pg_GSS_have_ccache(gss_cred_id_t *cred_out);
int pg_GSS_load_servicename(PGconn *conn);
#endif /* FE_GSSAPI_COMMON_H */
This diff is collapsed.
......@@ -220,6 +220,13 @@ pqsecure_read(PGconn *conn, void *ptr, size_t len)
n = pgtls_read(conn, ptr, len);
}
else
#endif
#ifdef ENABLE_GSS
if (conn->gssenc)
{
n = pg_GSS_read(conn, ptr, len);
}
else
#endif
{
n = pqsecure_raw_read(conn, ptr, len);
......@@ -297,6 +304,13 @@ pqsecure_write(PGconn *conn, const void *ptr, size_t len)
n = pgtls_write(conn, ptr, len);
}
else
#endif
#ifdef ENABLE_GSS
if (conn->gssenc)
{
n = pg_GSS_write(conn, ptr, len);
}
else
#endif
{
n = pqsecure_raw_write(conn, ptr, len);
......@@ -420,6 +434,23 @@ PQsslAttributeNames(PGconn *conn)
}
#endif /* USE_SSL */
/* Dummy version of GSSAPI information functions, when built without GSS support */
#ifndef ENABLE_GSS
void *
PQgetgssctx(PGconn *conn)
{
return NULL;
}
int
PQgssEncInUse(PGconn *conn)
{
return 0;
}
#endif /* ENABLE_GSS */
#if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
......
......@@ -65,8 +65,9 @@ typedef enum
CONNECTION_NEEDED, /* Internal state: connect() needed */
CONNECTION_CHECK_WRITABLE, /* Check if we could make a writable
* connection. */
CONNECTION_CONSUME /* Wait for any pending message and consume
CONNECTION_CONSUME, /* Wait for any pending message and consume
* them. */
CONNECTION_GSS_STARTUP /* Negotiating GSSAPI. */
} ConnStatusType;
typedef enum
......@@ -346,6 +347,12 @@ extern void PQinitSSL(int do_init);
/* More detailed way to tell libpq whether it needs to initialize OpenSSL */
extern void PQinitOpenSSL(int do_ssl, int do_crypto);
/* Return true if GSSAPI encryption is in use */
extern int PQgssEncInUse(PGconn *conn);
/* Returns GSSAPI context if GSSAPI is in use */
extern void *PQgetgssctx(PGconn *conn);
/* Set verbosity for PQerrorMessage and PQresultErrorMessage */
extern PGVerbosity PQsetErrorVerbosity(PGconn *conn, PGVerbosity verbosity);
......
......@@ -480,9 +480,15 @@ struct pg_conn
#endif /* USE_OPENSSL */
#endif /* USE_SSL */
char *gssencmode; /* GSS mode (require,prefer,disable) */
#ifdef ENABLE_GSS
gss_ctx_id_t gctx; /* GSS context */
gss_name_t gtarg_nam; /* GSS target name */
/* The following are encryption-only */
bool try_gss; /* GSS attempting permitted */
bool gssenc; /* GSS encryption is usable */
gss_cred_id_t gcred; /* GSS credential temp storage. */
#endif
#ifdef ENABLE_SSPI
......@@ -749,6 +755,23 @@ extern int pgtls_verify_peer_name_matches_certificate_guts(PGconn *conn,
int *names_examined,
char **first_name);
/* === GSSAPI === */
#ifdef ENABLE_GSS
/*
* Establish a GSSAPI-encrypted connection.
*/
extern PostgresPollingStatusType pqsecure_open_gss(PGconn *conn);
/*
* Read and write functions for GSSAPI-encrypted connections, with internal
* buffering to handle nonblocking sockets.
*/
extern ssize_t pg_GSS_write(PGconn *conn, const void *ptr, size_t len);
extern ssize_t pg_GSS_read(PGconn *conn, void *ptr, size_t len);
#endif
/* === miscellaneous macros === */
/*
......
use strict;
use warnings;
use TestLib;
use PostgresNode;
use Test::More;
use File::Path 'remove_tree';
if ($ENV{with_gssapi} eq 'yes')
{
plan tests => 5;
}
else
{
plan skip_all => 'GSSAPI/Kerberos not supported by this build';
}
my ($krb5_bin_dir, $krb5_sbin_dir);
if ($^O eq 'darwin')
{
$krb5_bin_dir = '/usr/local/opt/krb5/bin';
$krb5_sbin_dir = '/usr/local/opt/krb5/sbin';
}
elsif ($^O eq 'freebsd')
{
$krb5_bin_dir = '/usr/local/bin';
$krb5_sbin_dir = '/usr/local/sbin';
}
elsif ($^O eq 'linux')
{
$krb5_sbin_dir = '/usr/sbin';
}
my $krb5_config = 'krb5-config';
my $kinit = 'kinit';
my $kdb5_util = 'kdb5_util';
my $kadmin_local = 'kadmin.local';
my $krb5kdc = 'krb5kdc';
if ($krb5_bin_dir && -d $krb5_bin_dir)
{
$krb5_config = $krb5_bin_dir . '/' . $krb5_config;
$kinit = $krb5_bin_dir . '/' . $kinit;
}
if ($krb5_sbin_dir && -d $krb5_sbin_dir)
{
$kdb5_util = $krb5_sbin_dir . '/' . $kdb5_util;
$kadmin_local = $krb5_sbin_dir . '/' . $kadmin_local;
$krb5kdc = $krb5_sbin_dir . '/' . $krb5kdc;
}
my $host = 'auth-test-localhost.postgresql.example.com';
my $hostaddr = '127.0.0.1';
my $realm = 'EXAMPLE.COM';
my $krb5_conf = "${TestLib::tmp_check}/krb5.conf";
my $kdc_conf = "${TestLib::tmp_check}/kdc.conf";
my $krb5_log = "${TestLib::tmp_check}/krb5libs.log";
my $kdc_log = "${TestLib::tmp_check}/krb5kdc.log";
my $kdc_port = int(rand() * 16384) + 49152;
my $kdc_datadir = "${TestLib::tmp_check}/krb5kdc";
my $kdc_pidfile = "${TestLib::tmp_check}/krb5kdc.pid";
my $keytab = "${TestLib::tmp_check}/krb5.keytab";
note "setting up Kerberos";
my ($stdout, $krb5_version);
run_log [ $krb5_config, '--version' ], '>', \$stdout
or BAIL_OUT("could not execute krb5-config");
BAIL_OUT("Heimdal is not supported") if $stdout =~ m/heimdal/;
$stdout =~ m/Kerberos 5 release ([0-9]+\.[0-9]+)/
or BAIL_OUT("could not get Kerberos version");
$krb5_version = $1;
append_to_file(
$krb5_conf,
qq![logging]
default = FILE:$krb5_log
kdc = FILE:$kdc_log
[libdefaults]
default_realm = $realm
[realms]
$realm = {
kdc = $hostaddr:$kdc_port
}!);
append_to_file(
$kdc_conf,
qq![kdcdefaults]
!);
# For new-enough versions of krb5, use the _listen settings rather
# than the _ports settings so that we can bind to localhost only.
if ($krb5_version >= 1.15)
{
append_to_file(
$kdc_conf,
qq!kdc_listen = $hostaddr:$kdc_port
kdc_tcp_listen = $hostaddr:$kdc_port
!);
}
else
{
append_to_file(
$kdc_conf,
qq!kdc_ports = $kdc_port
kdc_tcp_ports = $kdc_port
!);
}
append_to_file(
$kdc_conf,
qq!
[realms]
$realm = {
database_name = $kdc_datadir/principal
admin_keytab = FILE:$kdc_datadir/kadm5.keytab
acl_file = $kdc_datadir/kadm5.acl
key_stash_file = $kdc_datadir/_k5.$realm
}!);
remove_tree $kdc_datadir;
mkdir $kdc_datadir or die;
$ENV{'KRB5_CONFIG'} = $krb5_conf;
$ENV{'KRB5_KDC_PROFILE'} = $kdc_conf;
my $service_principal = "$ENV{with_krb_srvnam}/$host";
system_or_bail $kdb5_util, 'create', '-s', '-P', 'secret0';
my $test1_password = 'secret1';
system_or_bail $kadmin_local, '-q', "addprinc -pw $test1_password test1";
system_or_bail $kadmin_local, '-q', "addprinc -randkey $service_principal";
system_or_bail $kadmin_local, '-q', "ktadd -k $keytab $service_principal";
system_or_bail $krb5kdc, '-P', $kdc_pidfile;
END
{
kill 'INT', `cat $kdc_pidfile` if -f $kdc_pidfile;
}
note "setting up PostgreSQL instance";
my $node = get_new_node('node');
$node->init;
$node->append_conf('postgresql.conf', "listen_addresses = 'localhost'");
$node->append_conf('postgresql.conf', "krb_server_keyfile = '$keytab'");
$node->start;
$node->safe_psql('postgres', 'CREATE USER test1;');
note "running tests";
sub test_access
{
my ($node, $gssencmode, $expected_res, $test_name) = @_;
my $res = $node->psql(
"postgres",
"SELECT 1",
extra_params => [
"-d",
$node->connstr("postgres") . " host=$host hostaddr=$hostaddr gssencmode=$gssencmode",
"-U", "test1",
]);
is($res, $expected_res, $test_name);
return;
}
unlink($node->data_dir . "/pg_ident.conf");
$node->append_conf("pg_ident.conf", 'mymap /^(.*)@EXAMPLE.COM$ \1');
run_log [ $kinit, 'test1' ], \$test1_password or BAIL_OUT($?);
unlink($node->data_dir . '/pg_hba.conf');
$node->append_conf('pg_hba.conf',
qq{hostgssenc all all $hostaddr/32 gss map=mymap});
$node->restart;
test_access($node, "require", 0, "GSS-encrypted access");
test_access($node, "disable", 2, "GSS encryption disabled");
unlink($node->data_dir . "/pg_hba.conf");
$node->append_conf("pg_hba.conf", qq{hostgssenc all all $hostaddr/32 trust});
$node->restart;
test_access($node, "require", 0, "GSS encryption without auth");
unlink($node->data_dir . "/pg_hba.conf");
$node->append_conf("pg_hba.conf",
qq{hostnogssenc all all localhost gss map=mymap});
$node->restart;
test_access($node, "prefer", 0, "GSS unencrypted fallback");
# Check that the client can prevent fallback.
test_access($node, "require", 2, "GSS unencrypted fallback prevention");
......@@ -1739,7 +1739,7 @@ pg_stat_activity| SELECT s.datid,
s.backend_xmin,
s.query,
s.backend_type
FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, sslcompression, ssl_client_dn, ssl_client_serial, ssl_issuer_dn)
FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, sslcompression, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc)
LEFT JOIN pg_database d ON ((s.datid = d.oid)))
LEFT JOIN pg_authid u ON ((s.usesysid = u.oid)));
pg_stat_all_indexes| SELECT c.oid AS relid,
......@@ -1830,6 +1830,11 @@ pg_stat_database_conflicts| SELECT d.oid AS datid,
pg_stat_get_db_conflict_bufferpin(d.oid) AS confl_bufferpin,
pg_stat_get_db_conflict_startup_deadlock(d.oid) AS confl_deadlock
FROM pg_database d;
pg_stat_gssapi| SELECT s.pid,
s.gss_auth AS gss_authenticated,
s.gss_princ AS principal,
s.gss_enc AS encrypted
FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, sslcompression, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc);
pg_stat_progress_cluster| SELECT s.pid,
s.datid,
d.datname,
......@@ -1926,7 +1931,7 @@ pg_stat_replication| SELECT s.pid,
w.sync_priority,
w.sync_state,
w.reply_time
FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, sslcompression, ssl_client_dn, ssl_client_serial, ssl_issuer_dn)
FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, sslcompression, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc)
JOIN pg_stat_get_wal_senders() w(pid, state, sent_lsn, write_lsn, flush_lsn, replay_lsn, write_lag, flush_lag, replay_lag, sync_priority, sync_state, reply_time) ON ((s.pid = w.pid)))
LEFT JOIN pg_authid u ON ((s.usesysid = u.oid)));
pg_stat_ssl| SELECT s.pid,
......@@ -1938,7 +1943,7 @@ pg_stat_ssl| SELECT s.pid,
s.ssl_client_dn AS client_dn,
s.ssl_client_serial AS client_serial,
s.ssl_issuer_dn AS issuer_dn
FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, sslcompression, ssl_client_dn, ssl_client_serial, ssl_issuer_dn);
FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslbits, sslcompression, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc);
pg_stat_subscription| SELECT su.oid AS subid,
su.subname,
st.pid,
......
......@@ -194,6 +194,11 @@ sub mkvcbuild
$postgres->RemoveFile('src/backend/libpq/be-secure-common.c');
$postgres->RemoveFile('src/backend/libpq/be-secure-openssl.c');
}
if (!$solution->{options}->{gss})
{
$postgres->RemoveFile('src/backend/libpq/be-gssapi-common.c');
$postgres->RemoveFile('src/backend/libpq/be-secure-gssapi.c');
}
my $snowball = $solution->AddProject('dict_snowball', 'dll', '',
'src/backend/snowball');
......@@ -254,6 +259,11 @@ sub mkvcbuild
$libpq->RemoveFile('src/interfaces/libpq/fe-secure-common.c');
$libpq->RemoveFile('src/interfaces/libpq/fe-secure-openssl.c');
}
if (!$solution->{options}->{gss})
{
$libpq->RemoveFile('src/interfaces/libpq/fe-gssapi-common.c');
$libpq->RemoveFile('src/interfaces/libpq/fe-secure-gssapi.c');
}
my $libpqwalreceiver =
$solution->AddProject('libpqwalreceiver', 'dll', '',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment