gss_delegation_v2.patch
text/x-diff
Filename: gss_delegation_v2.patch
Type: text/x-diff
Part: 0
Patch
Format: unified
Series: patch v2
| File | + | − |
|---|---|---|
| contrib/dblink/dblink.c | 3 | 3 |
| contrib/postgres_fdw/expected/postgres_fdw.out | 2 | 1 |
| contrib/postgres_fdw/option.c | 3 | 0 |
| contrib/postgres_fdw/sql/postgres_fdw.sql | 2 | 1 |
| doc/src/sgml/config.sgml | 17 | 0 |
| doc/src/sgml/libpq.sgml | 41 | 0 |
| doc/src/sgml/monitoring.sgml | 9 | 0 |
| src/backend/catalog/system_views.sql | 2 | 1 |
| src/backend/foreign/foreign.c | 1 | 0 |
| src/backend/libpq/auth.c | 12 | 1 |
| src/backend/libpq/be-gssapi-common.c | 51 | 0 |
| src/backend/libpq/be-secure-gssapi.c | 25 | 1 |
| src/backend/utils/activity/backend_status.c | 1 | 0 |
| src/backend/utils/adt/pgstatfuncs.c | 12 | 8 |
| src/backend/utils/init/postinit.c | 5 | 3 |
| src/backend/utils/misc/guc_tables.c | 10 | 0 |
| src/backend/utils/misc/postgresql.conf.sample | 1 | 0 |
| src/include/catalog/pg_proc.dat | 3 | 3 |
| src/include/libpq/auth.h | 1 | 0 |
| src/include/libpq/be-gssapi-common.h | 3 | 0 |
| src/include/libpq/libpq-be.h | 2 | 0 |
| src/include/utils/backend_status.h | 1 | 0 |
| src/interfaces/libpq/exports.txt | 1 | 0 |
| src/interfaces/libpq/fe-auth.c | 15 | 3 |
| src/interfaces/libpq/fe-connect.c | 17 | 0 |
| src/interfaces/libpq/fe-secure-gssapi.c | 23 | 3 |
| src/interfaces/libpq/libpq-fe.h | 1 | 0 |
| src/interfaces/libpq/libpq-int.h | 2 | 0 |
| src/test/kerberos/Makefile | 3 | 0 |
| src/test/kerberos/t/001_auth.pl | 171 | 25 |
| src/test/perl/PostgreSQL/Test/Utils.pm | 27 | 0 |
| src/test/regress/expected/rules.out | 6 | 5 |
diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index 78a8bcee6e..713ef7c248 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -2589,7 +2589,7 @@ dblink_security_check(PGconn *conn, remoteConn *rconn)
{
if (!superuser())
{
- if (!PQconnectionUsedPassword(conn))
+ if (!(PQconnectionUsedPassword(conn) || PQconnectionUsedGSSAPI(conn)))
{
libpqsrv_disconnect(conn);
if (rconn)
@@ -2597,8 +2597,8 @@ dblink_security_check(PGconn *conn, remoteConn *rconn)
ereport(ERROR,
(errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED),
- errmsg("password is required"),
- errdetail("Non-superuser cannot connect if the server does not request a password."),
+ errmsg("password or GSSAPI is required"),
+ errdetail("Non-superuser cannot connect if the server does not request a password or use GSSAPI."),
errhint("Target server's authentication method must be changed.")));
}
}
diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index d5fc61446a..77656ccf80 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -171,7 +171,8 @@ ALTER SERVER testserver1 OPTIONS (
sslcrl 'value',
--requirepeer 'value',
krbsrvname 'value',
- gsslib 'value'
+ gsslib 'value',
+ gssdeleg 'value'
--replication 'value'
);
-- Error, invalid list syntax
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index 984e4d168a..bb6f309907 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -286,6 +286,9 @@ InitPgFdwOptions(void)
{"sslcert", UserMappingRelationId, true},
{"sslkey", UserMappingRelationId, true},
+ /* gssencmode is also libpq option, same to above. */
+ {"gssencmode", UserMappingRelationId, true},
+
{NULL, InvalidOid, false}
};
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index 1e50be137b..1a43bdf55b 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -185,7 +185,8 @@ ALTER SERVER testserver1 OPTIONS (
sslcrl 'value',
--requirepeer 'value',
krbsrvname 'value',
- gsslib 'value'
+ gsslib 'value',
+ gssdeleg 'value'
--replication 'value'
);
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index ecd9aa73ef..a931996968 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -1170,6 +1170,23 @@ include_dir 'conf.d'
</listitem>
</varlistentry>
+ <varlistentry id="guc-gss-accept-deleg" xreflabel="gss_accept_deleg">
+ <term><varname>gss_accept_deleg</varname> (<type>boolean</type>)
+ <indexterm>
+ <primary><varname>gss_accept_deleg</varname> configuration parameter</primary>
+ </indexterm>
+ </term>
+ <listitem>
+ <para>
+ Sets whether GSSAPI delegation should be accepted from the client.
+ The default is <literal>off</literal> meaning credentials from the client will
+ NOT be accepted. Changing this to <literal>on</literal> will make the server
+ accept credentials delegated to it from the client. This parameter can only be
+ set in the <filename>postgresql.conf</filename> file or on the server command line.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry id="guc-db-user-namespace" xreflabel="db_user_namespace">
<term><varname>db_user_namespace</varname> (<type>boolean</type>)
<indexterm>
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 0e7ae70c70..9f3e12b2a7 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1879,6 +1879,18 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
</listitem>
</varlistentry>
+ <varlistentry id="libpq-connect-gssdeleg" xreflabel="gssdeleg">
+ <term><literal>gssdeleg</literal></term>
+ <listitem>
+ <para>
+ Forward (delegate) GSS credentials to the server. The default is
+ <literal>disable</literal> which means credentials will not be forwarded
+ to the server. Set this to <literal>enable</literal> to have
+ credentials forwarded when possible.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry id="libpq-connect-service" xreflabel="service">
<term><literal>service</literal></term>
<listitem>
@@ -2479,6 +2491,25 @@ int PQconnectionUsedPassword(const PGconn *conn);
</para>
</listitem>
</varlistentry>
+
+ <varlistentry id="libpq-PQconnectionUsedGSSAPI">
+ <term><function>PQconnectionUsedGSSAPI</function><indexterm><primary>PQconnectionUsedGSSAPI</primary></indexterm></term>
+ <listitem>
+ <para>
+ Returns true (1) if the connection authentication method
+ used GSSAPI. Returns false (0) if not.
+
+<synopsis>
+int PQconnectionUsedGSSAPI(const PGconn *conn);
+</synopsis>
+ </para>
+
+ <para>
+ This function can be applied to detect whether the connection was
+ authenticated with GSSAPI.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</para>
@@ -7981,6 +8012,16 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
</para>
</listitem>
+ <listitem>
+ <para>
+ <indexterm>
+ <primary><envar>PGGSSDELEG</envar></primary>
+ </indexterm>
+ <envar>PGGSSDELEG</envar> behaves the same as the <xref
+ linkend="libpq-connect-gssdeleg"/> connection parameter.
+ </para>
+ </listitem>
+
<listitem>
<para>
<indexterm>
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index b0b997f092..48cc5608b6 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -3573,6 +3573,15 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
True if GSSAPI encryption is in use on this connection
</para></entry>
</row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>credentials_proxied</structfield> <type>boolean</type>
+ </para>
+ <para>
+ True if GSSAPI credentials were forwarded/proxied on this connection.
+ </para></entry>
+ </row>
</tbody>
</tgroup>
</table>
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 34ca0e739f..1843de1c77 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -977,7 +977,8 @@ CREATE VIEW pg_stat_gssapi AS
S.pid,
S.gss_auth AS gss_authenticated,
S.gss_princ AS principal,
- S.gss_enc AS encrypted
+ S.gss_enc AS encrypted,
+ S.gss_proxy AS credentials_proxied
FROM pg_stat_get_activity(NULL) AS S
WHERE S.client_port IS NOT NULL;
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c
index dca02271dc..6e1977fa62 100644
--- a/src/backend/foreign/foreign.c
+++ b/src/backend/foreign/foreign.c
@@ -574,6 +574,7 @@ static const struct ConnectionOption libpq_conninfo_options[] = {
{"requiressl", ForeignServerRelationId},
{"sslmode", ForeignServerRelationId},
{"gsslib", ForeignServerRelationId},
+ {"gssdeleg", ForeignServerRelationId},
{NULL, InvalidOid}
};
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 25b3a781cd..963bc26b97 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -161,6 +161,7 @@ static int CheckCertAuth(Port *port);
*/
char *pg_krb_server_keyfile;
bool pg_krb_caseins_users;
+bool pg_gss_accept_deleg;
/*----------------------------------------------------------------
@@ -914,6 +915,7 @@ pg_GSS_recvauth(Port *port)
int mtype;
StringInfoData buf;
gss_buffer_desc gbuf;
+ gss_cred_id_t proxy;
/*
* Use the configured keytab, if there is one. Unfortunately, Heimdal
@@ -943,6 +945,9 @@ pg_GSS_recvauth(Port *port)
*/
port->gss->ctx = GSS_C_NO_CONTEXT;
+ proxy = NULL;
+ port->gss->proxy_creds = false;
+
/*
* Loop through GSSAPI message exchange. This exchange can consist of
* multiple messages sent in both directions. First message is always from
@@ -993,7 +998,7 @@ pg_GSS_recvauth(Port *port)
&port->gss->outbuf,
&gflags,
NULL,
- NULL);
+ pg_gss_accept_deleg ? &proxy : NULL);
/* gbuf no longer used */
pfree(buf.data);
@@ -1005,6 +1010,12 @@ pg_GSS_recvauth(Port *port)
CHECK_FOR_INTERRUPTS();
+ if (proxy != NULL)
+ {
+ pg_store_proxy_credential(proxy);
+ port->gss->proxy_creds = true;
+ }
+
if (port->gss->outbuf.length != 0)
{
/*
diff --git a/src/backend/libpq/be-gssapi-common.c b/src/backend/libpq/be-gssapi-common.c
index fb39c760d8..d5a9926d5c 100644
--- a/src/backend/libpq/be-gssapi-common.c
+++ b/src/backend/libpq/be-gssapi-common.c
@@ -92,3 +92,54 @@ pg_GSS_error(const char *errmsg,
(errmsg_internal("%s", errmsg),
errdetail_internal("%s: %s", msg_major, msg_minor)));
}
+
+/*
+ * Store the credentials passed in into the memory cache for later usage.
+ *
+ * This allows credentials to be delegated to us for us to use to connect
+ * to other systems with, using, e.g. postgres_fdw or dblink.
+ */
+#define GSS_MEMORY_CACHE "MEMORY:"
+void
+pg_store_proxy_credential(gss_cred_id_t cred)
+{
+ OM_uint32 major, minor;
+ gss_OID_set mech;
+ gss_cred_usage_t usage;
+ gss_key_value_element_desc cc;
+ gss_key_value_set_desc ccset;
+
+ cc.key = "ccache";
+ cc.value = GSS_MEMORY_CACHE;
+ ccset.count = 1;
+ ccset.elements = &cc;
+
+ /* Make the proxy credential only available to current process */
+ major = gss_store_cred_into(&minor,
+ cred,
+ GSS_C_INITIATE, /* credential only used for starting libpq connection */
+ GSS_C_NULL_OID, /* store all */
+ true, /* overwrite */
+ true, /* make default */
+ &ccset,
+ &mech,
+ &usage);
+
+ if (major != GSS_S_COMPLETE)
+ {
+ pg_GSS_error("gss_store_cred", major, minor);
+ }
+
+ /* Credential stored, so we can release our credential handle. */
+ major = gss_release_cred(&minor, &cred);
+ if (major != GSS_S_COMPLETE)
+ {
+ pg_GSS_error("gss_release_cred", major, minor);
+ }
+
+ /*
+ * Set KRB5CCNAME for this backend, so that later calls to gss_acquire_cred
+ * will find the proxied credentials we stored.
+ */
+ setenv("KRB5CCNAME", GSS_MEMORY_CACHE, 1);
+}
diff --git a/src/backend/libpq/be-secure-gssapi.c b/src/backend/libpq/be-secure-gssapi.c
index 3b55f43199..9ff91534e1 100644
--- a/src/backend/libpq/be-secure-gssapi.c
+++ b/src/backend/libpq/be-secure-gssapi.c
@@ -497,6 +497,7 @@ secure_open_gssapi(Port *port)
bool complete_next = false;
OM_uint32 major,
minor;
+ gss_cred_id_t proxy;
/*
* Allocate subsidiary Port data for GSSAPI operations.
@@ -504,6 +505,9 @@ secure_open_gssapi(Port *port)
port->gss = (pg_gssinfo *)
MemoryContextAllocZero(TopMemoryContext, sizeof(pg_gssinfo));
+ proxy = NULL;
+ port->gss->proxy_creds = false;
+
/*
* Allocate buffers and initialize state variables. By malloc'ing the
* buffers at this point, we avoid wasting static data space in processes
@@ -588,7 +592,8 @@ secure_open_gssapi(Port *port)
GSS_C_NO_CREDENTIAL, &input,
GSS_C_NO_CHANNEL_BINDINGS,
&port->gss->name, NULL, &output, NULL,
- NULL, NULL);
+ NULL, pg_gss_accept_deleg ? &proxy : NULL);
+
if (GSS_ERROR(major))
{
pg_GSS_error(_("could not accept GSSAPI security context"),
@@ -605,6 +610,12 @@ secure_open_gssapi(Port *port)
complete_next = true;
}
+ if (proxy != NULL)
+ {
+ pg_store_proxy_credential(proxy);
+ port->gss->proxy_creds = true;
+ }
+
/* Done handling the incoming packet, reset our buffer */
PqGSSRecvLength = 0;
@@ -731,3 +742,16 @@ be_gssapi_get_princ(Port *port)
return port->gss->princ;
}
+
+/*
+ * Return if GSSAPI delegated/proxy credentials were included on this
+ * connection.
+ */
+bool
+be_gssapi_get_proxy(Port *port)
+{
+ if (!port || !port->gss)
+ return NULL;
+
+ return port->gss->proxy_creds;
+}
diff --git a/src/backend/utils/activity/backend_status.c b/src/backend/utils/activity/backend_status.c
index 608d01ea0d..778d5769fd 100644
--- a/src/backend/utils/activity/backend_status.c
+++ b/src/backend/utils/activity/backend_status.c
@@ -384,6 +384,7 @@ pgstat_bestart(void)
lbeentry.st_gss = true;
lgssstatus.gss_auth = be_gssapi_get_auth(MyProcPort);
lgssstatus.gss_enc = be_gssapi_get_enc(MyProcPort);
+ lgssstatus.gss_proxy = be_gssapi_get_proxy(MyProcPort);
if (princ)
strlcpy(lgssstatus.gss_princ, princ, NAMEDATALEN);
}
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 9d707c3521..cf774a6848 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -303,7 +303,7 @@ pg_stat_get_progress_info(PG_FUNCTION_ARGS)
Datum
pg_stat_get_activity(PG_FUNCTION_ARGS)
{
-#define PG_STAT_GET_ACTIVITY_COLS 30
+#define PG_STAT_GET_ACTIVITY_COLS 31
int num_backends = pgstat_fetch_stat_numbackends();
int curr_backend;
int pid = PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0);
@@ -395,7 +395,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
pfree(clipped_activity);
/* leader_pid */
- nulls[28] = true;
+ nulls[29] = true;
proc = BackendPidGetProc(beentry->st_procpid);
@@ -432,8 +432,8 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
*/
if (leader && leader->pid != beentry->st_procpid)
{
- values[28] = Int32GetDatum(leader->pid);
- nulls[28] = false;
+ values[29] = Int32GetDatum(leader->pid);
+ nulls[29] = false;
}
else if (beentry->st_backendType == B_BG_WORKER)
{
@@ -441,8 +441,8 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
if (leader_pid != InvalidPid)
{
- values[28] = Int32GetDatum(leader_pid);
- nulls[28] = false;
+ values[29] = Int32GetDatum(leader_pid);
+ nulls[29] = false;
}
}
}
@@ -600,6 +600,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
values[25] = BoolGetDatum(beentry->st_gssstatus->gss_auth); /* gss_auth */
values[26] = CStringGetTextDatum(beentry->st_gssstatus->gss_princ);
values[27] = BoolGetDatum(beentry->st_gssstatus->gss_enc); /* GSS Encryption in use */
+ values[28] = BoolGetDatum(beentry->st_gssstatus->gss_proxy); /* GSS credentials proxied */
}
else
{
@@ -607,11 +608,13 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
nulls[26] = true; /* No GSS principal */
values[27] = BoolGetDatum(false); /* GSS Encryption not in
* use */
+ values[28] = BoolGetDatum(false); /* GSS credentials not
+ * proxied */
}
if (beentry->st_query_id == 0)
- nulls[29] = true;
+ nulls[30] = true;
else
- values[29] = UInt64GetDatum(beentry->st_query_id);
+ values[30] = UInt64GetDatum(beentry->st_query_id);
}
else
{
@@ -640,6 +643,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
nulls[27] = true;
nulls[28] = true;
nulls[29] = true;
+ nulls[30] = true;
}
tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 2f07ca7a0e..c872512bbc 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -282,15 +282,17 @@ PerformAuthentication(Port *port)
if (princ)
appendStringInfo(&logmsg,
- _(" GSS (authenticated=%s, encrypted=%s, principal=%s)"),
+ _(" GSS (authenticated=%s, encrypted=%s, proxy_credentials=%s, principal=%s)"),
be_gssapi_get_auth(port) ? _("yes") : _("no"),
be_gssapi_get_enc(port) ? _("yes") : _("no"),
+ be_gssapi_get_proxy(port) ? _("yes") : _("no"),
princ);
else
appendStringInfo(&logmsg,
- _(" GSS (authenticated=%s, encrypted=%s)"),
+ _(" GSS (authenticated=%s, encrypted=%s, proxy_credentials=%s)"),
be_gssapi_get_auth(port) ? _("yes") : _("no"),
- be_gssapi_get_enc(port) ? _("yes") : _("no"));
+ be_gssapi_get_enc(port) ? _("yes") : _("no"),
+ be_gssapi_get_proxy(port) ? _("yes") : _("no"));
}
#endif
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 1c0583fe26..6d7d5908ef 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -1708,6 +1708,16 @@ struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
+ {
+ {"gss_accept_deleg", PGC_SIGHUP, CONN_AUTH_AUTH,
+ gettext_noop("Sets whether GSSAPI delegation should be accepted from the client."),
+ NULL
+ },
+ &pg_gss_accept_deleg,
+ false,
+ NULL, NULL, NULL
+ },
+
{
{"escape_string_warning", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("Warn about backslash escapes in ordinary string literals."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index d06074b86f..7d26d58de3 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -100,6 +100,7 @@
# GSSAPI using Kerberos
#krb_server_keyfile = 'FILE:${sysconfdir}/krb5.keytab'
#krb_caseins_users = off
+#gss_accept_deleg = off
# - SSL -
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 66b73c3900..44f05cfac0 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5404,9 +5404,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,text,numeric,text,bool,text,bool,int4,int8}',
- 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,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,ssl_client_dn,ssl_client_serial,ssl_issuer_dn,gss_auth,gss_princ,gss_enc,leader_pid,query_id}',
+ proallargtypes => '{int4,oid,int4,oid,text,text,text,text,text,timestamptz,timestamptz,timestamptz,timestamptz,inet,text,int4,xid,xid,text,bool,text,text,int4,text,numeric,text,bool,text,bool,bool,int4,int8}',
+ 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,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,ssl_client_dn,ssl_client_serial,ssl_issuer_dn,gss_auth,gss_princ,gss_enc,gss_proxy,leader_pid,query_id}',
prosrc => 'pg_stat_get_activity' },
{ oid => '3318',
descr => 'statistics: information about progress of backends running maintenance command',
diff --git a/src/include/libpq/auth.h b/src/include/libpq/auth.h
index 137bee7c45..9105d6757d 100644
--- a/src/include/libpq/auth.h
+++ b/src/include/libpq/auth.h
@@ -18,6 +18,7 @@
extern PGDLLIMPORT char *pg_krb_server_keyfile;
extern PGDLLIMPORT bool pg_krb_caseins_users;
+extern PGDLLIMPORT bool pg_gss_accept_deleg;
extern PGDLLIMPORT char *pg_krb_realm;
extern void ClientAuthentication(Port *port);
diff --git a/src/include/libpq/be-gssapi-common.h b/src/include/libpq/be-gssapi-common.h
index facd24ff7f..841facde12 100644
--- a/src/include/libpq/be-gssapi-common.h
+++ b/src/include/libpq/be-gssapi-common.h
@@ -18,13 +18,16 @@
#if defined(HAVE_GSSAPI_H)
#include <gssapi.h>
+#include <gssapi_ext.h>
#else
#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_ext.h>
#endif
extern void pg_GSS_error(const char *errmsg,
OM_uint32 maj_stat, OM_uint32 min_stat);
+extern void pg_store_proxy_credential(gss_cred_id_t cred);
#endif /* ENABLE_GSS */
#endif /* BE_GSSAPI_COMMON_H */
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index ac6407e9f6..4d6a8c31cb 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -84,6 +84,7 @@ typedef struct
* GSSAPI auth was not used */
bool auth; /* GSSAPI Authentication used */
bool enc; /* GSSAPI encryption in use */
+ bool proxy_creds; /* GSSAPI Delegated/proxy credentials */
#endif
} pg_gssinfo;
#endif
@@ -328,6 +329,7 @@ extern PGDLLIMPORT openssl_tls_init_hook_typ openssl_tls_init_hook;
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);
+extern bool be_gssapi_get_proxy(Port *port);
/* Read and write to a GSSAPI-encrypted connection. */
extern ssize_t be_gssapi_read(Port *port, void *ptr, size_t len);
diff --git a/src/include/utils/backend_status.h b/src/include/utils/backend_status.h
index f7bd83113a..0e680f0332 100644
--- a/src/include/utils/backend_status.h
+++ b/src/include/utils/backend_status.h
@@ -77,6 +77,7 @@ typedef struct PgBackendGSSStatus
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 */
+ bool gss_proxy; /* If credentials proxied */
} PgBackendGSSStatus;
diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt
index e8bcc88370..7ded77aff3 100644
--- a/src/interfaces/libpq/exports.txt
+++ b/src/interfaces/libpq/exports.txt
@@ -186,3 +186,4 @@ PQpipelineStatus 183
PQsetTraceFlags 184
PQmblenBounded 185
PQsendFlushRequest 186
+PQconnectionUsedGSSAPI 187
diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c
index 9afc6f19b9..a518378915 100644
--- a/src/interfaces/libpq/fe-auth.c
+++ b/src/interfaces/libpq/fe-auth.c
@@ -58,9 +58,11 @@ pg_GSS_continue(PGconn *conn, int payloadlen)
{
OM_uint32 maj_stat,
min_stat,
- lmin_s;
+ lmin_s,
+ gss_flags = GSS_C_MUTUAL_FLAG;
gss_buffer_desc ginbuf;
gss_buffer_desc goutbuf;
+ gss_cred_id_t proxy = GSS_C_NO_CREDENTIAL;
/*
* On first call, there's no input token. On subsequent calls, read the
@@ -92,12 +94,19 @@ pg_GSS_continue(PGconn *conn, int payloadlen)
ginbuf.value = NULL;
}
+ /* Only try to acquire credentials if GSS delegation isn't disabled. */
+ if (!pg_GSS_have_cred_cache(&proxy))
+ proxy = GSS_C_NO_CREDENTIAL;
+
+ if (conn->gssdeleg && pg_strcasecmp(conn->gssdeleg,"enable") == 0)
+ gss_flags |= GSS_C_DELEG_FLAG;
+
maj_stat = gss_init_sec_context(&min_stat,
- GSS_C_NO_CREDENTIAL,
+ proxy,
&conn->gctx,
conn->gtarg_nam,
GSS_C_NO_OID,
- GSS_C_MUTUAL_FLAG,
+ gss_flags,
0,
GSS_C_NO_CHANNEL_BINDINGS,
(ginbuf.value == NULL) ? GSS_C_NO_BUFFER : &ginbuf,
@@ -136,7 +145,10 @@ pg_GSS_continue(PGconn *conn, int payloadlen)
}
if (maj_stat == GSS_S_COMPLETE)
+ {
gss_release_name(&lmin_s, &conn->gtarg_nam);
+ conn->gssapi_used = true;
+ }
return STATUS_OK;
}
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 50b5df3490..69c7e1346a 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -332,6 +332,10 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
"GSS-library", "", 7, /* sizeof("gssapi") == 7 */
offsetof(struct pg_conn, gsslib)},
+ {"gssdeleg", "PGGSSDELEG", NULL, NULL,
+ "GSS-delegation", "", 8, /* sizeof("disable") == 8 */
+ offsetof(struct pg_conn, gssdeleg)},
+
{"replication", NULL, NULL, NULL,
"Replication", "D", 5,
offsetof(struct pg_conn, replication)},
@@ -596,6 +600,7 @@ pqDropServerData(PGconn *conn)
conn->last_sqlstate[0] = '\0';
conn->auth_req_received = false;
conn->password_needed = false;
+ conn->gssapi_used = false;
conn->write_failed = false;
free(conn->write_err_msg);
conn->write_err_msg = NULL;
@@ -4028,6 +4033,7 @@ freePGconn(PGconn *conn)
free(conn->gssencmode);
free(conn->krbsrvname);
free(conn->gsslib);
+ free(conn->gssdeleg);
free(conn->connip);
/* Note that conn->Pfdebug is not ours to close or free */
free(conn->write_err_msg);
@@ -6816,6 +6822,17 @@ PQconnectionUsedPassword(const PGconn *conn)
return false;
}
+int
+PQconnectionUsedGSSAPI(const PGconn *conn)
+{
+ if (!conn)
+ return false;
+ if (conn->gssapi_used)
+ return true;
+ else
+ return false;
+}
+
int
PQclientEncoding(const PGconn *conn)
{
diff --git a/src/interfaces/libpq/fe-secure-gssapi.c b/src/interfaces/libpq/fe-secure-gssapi.c
index 038e847b7e..372bfdd21e 100644
--- a/src/interfaces/libpq/fe-secure-gssapi.c
+++ b/src/interfaces/libpq/fe-secure-gssapi.c
@@ -477,11 +477,13 @@ pqsecure_open_gss(PGconn *conn)
{
ssize_t ret;
OM_uint32 major,
- minor;
+ minor,
+ gss_flags = GSS_REQUIRED_FLAGS;
uint32 netlen;
PostgresPollingStatusType result;
gss_buffer_desc input = GSS_C_EMPTY_BUFFER,
output = GSS_C_EMPTY_BUFFER;
+ static gss_cred_id_t proxy = GSS_C_NO_CREDENTIAL;
/*
* If first time through for this connection, allocate buffers and
@@ -621,13 +623,30 @@ pqsecure_open_gss(PGconn *conn)
if (ret != STATUS_OK)
return PGRES_POLLING_FAILED;
+ if (conn->gssdeleg && pg_strcasecmp(conn->gssdeleg,"enable") == 0)
+ {
+ /* Acquire credentials if possbile */
+ if (proxy == GSS_C_NO_CREDENTIAL)
+ (void) pg_GSS_have_cred_cache(&proxy);
+
+ /*
+ * We have credentials and gssdeleg is enabled, so request
+ * credential delegation. This may or may not actually result in
+ * credentials being delegated- it depends on if the forwardable
+ * flag has been set in the credential and if the server is
+ * configured to accept delegated credentials.
+ */
+ if (proxy != GSS_C_NO_CREDENTIAL)
+ gss_flags |= GSS_C_DELEG_FLAG;
+ }
+
/*
* Call GSS init context, either with an empty input, or with a complete
* packet from the server.
*/
- major = gss_init_sec_context(&minor, conn->gcred, &conn->gctx,
+ major = gss_init_sec_context(&minor, proxy, &conn->gctx,
conn->gtarg_nam, GSS_C_NO_OID,
- GSS_REQUIRED_FLAGS, 0, 0, &input, NULL,
+ gss_flags, 0, 0, &input, NULL,
&output, NULL, NULL);
/* GSS Init Sec Context uses the whole packet, so clear it */
@@ -647,6 +666,7 @@ pqsecure_open_gss(PGconn *conn)
* to do GSS wrapping/unwrapping.
*/
conn->gssenc = true;
+ conn->gssapi_used = true;
/* Clean up */
gss_release_cred(&minor, &conn->gcred);
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index f3d9220496..7476dbe0e9 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -354,6 +354,7 @@ extern int PQbackendPID(const PGconn *conn);
extern PGpipelineStatus PQpipelineStatus(const PGconn *conn);
extern int PQconnectionNeedsPassword(const PGconn *conn);
extern int PQconnectionUsedPassword(const PGconn *conn);
+extern int PQconnectionUsedGSSAPI(const PGconn *conn);
extern int PQclientEncoding(const PGconn *conn);
extern int PQsetClientEncoding(PGconn *conn, const char *encoding);
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index d7ec5ed429..4828917d38 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -393,6 +393,7 @@ struct pg_conn
char *krbsrvname; /* Kerberos service name */
char *gsslib; /* What GSS library to use ("gssapi" or
* "sspi") */
+ char *gssdeleg; /* Try to delegate GSS credentials? */
char *ssl_min_protocol_version; /* minimum TLS protocol version */
char *ssl_max_protocol_version; /* maximum TLS protocol version */
char *target_session_attrs; /* desired session properties */
@@ -452,6 +453,7 @@ struct pg_conn
int sversion; /* server version, e.g. 70401 for 7.4.1 */
bool auth_req_received; /* true if any type of auth req received */
bool password_needed; /* true if server demanded a password */
+ bool gssapi_used; /* true if authenticated via gssapi */
bool sigpipe_so; /* have we masked SIGPIPE via SO_NOSIGPIPE? */
bool sigpipe_flag; /* can we mask SIGPIPE via MSG_NOSIGNAL? */
bool write_failed; /* have we had a write failure on sock? */
diff --git a/src/test/kerberos/Makefile b/src/test/kerberos/Makefile
index 7765f3f93b..f460d2c0e7 100644
--- a/src/test/kerberos/Makefile
+++ b/src/test/kerberos/Makefile
@@ -13,6 +13,9 @@ subdir = src/test/kerberos
top_builddir = ../../..
include $(top_builddir)/src/Makefile.global
+EXTRA_INSTALL += contrib/postgres_fdw
+EXTRA_INSTALL += contrib/dblink
+
export with_gssapi with_krb_srvnam
check:
diff --git a/src/test/kerberos/t/001_auth.pl b/src/test/kerberos/t/001_auth.pl
index d610ce63ab..6e76ef6373 100644
--- a/src/test/kerberos/t/001_auth.pl
+++ b/src/test/kerberos/t/001_auth.pl
@@ -56,6 +56,7 @@ elsif ($^O eq 'linux')
my $krb5_config = 'krb5-config';
my $kinit = 'kinit';
+my $klist = 'klist';
my $kdb5_util = 'kdb5_util';
my $kadmin_local = 'kadmin.local';
my $krb5kdc = 'krb5kdc';
@@ -64,6 +65,7 @@ if ($krb5_bin_dir && -d $krb5_bin_dir)
{
$krb5_config = $krb5_bin_dir . '/' . $krb5_config;
$kinit = $krb5_bin_dir . '/' . $kinit;
+ $klist = $krb5_bin_dir . '/' . $klist;
}
if ($krb5_sbin_dir && -d $krb5_sbin_dir)
{
@@ -108,6 +110,7 @@ kdc = FILE:$kdc_log
[libdefaults]
default_realm = $realm
+forwardable = false
[realms]
$realm = {
@@ -185,7 +188,21 @@ lc_messages = 'C'
});
$node->start;
+my $port = $node->port();
+
$node->safe_psql('postgres', 'CREATE USER test1;');
+$node->safe_psql('postgres', 'CREATE EXTENSION postgres_fdw;');
+$node->safe_psql('postgres', 'CREATE EXTENSION dblink;');
+$node->safe_psql('postgres', "CREATE SERVER s1 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '$host', hostaddr '$hostaddr', port '$port', dbname 'postgres');");
+
+$node->safe_psql('postgres', 'GRANT USAGE ON FOREIGN SERVER s1 TO test1;');
+
+$node->safe_psql('postgres', "CREATE USER MAPPING FOR test1 SERVER s1 OPTIONS (user 'test1', password_required 'false');");
+$node->safe_psql('postgres', "CREATE TABLE t1 (c1 int);");
+$node->safe_psql('postgres', "INSERT INTO t1 VALUES (1);");
+$node->safe_psql('postgres', "CREATE FOREIGN TABLE tf1 (c1 int) SERVER s1 OPTIONS (schema_name 'public', table_name 't1');");
+$node->safe_psql('postgres', "GRANT SELECT ON t1 TO test1;");
+$node->safe_psql('postgres', "GRANT SELECT ON tf1 TO test1;");
# Set up a table for SYSTEM_USER parallel worker testing.
$node->safe_psql('postgres',
@@ -258,6 +275,7 @@ $node->restart;
test_access($node, 'test1', 'SELECT true', 2, '', 'fails without ticket');
run_log [ $kinit, 'test1' ], \$test1_password or BAIL_OUT($?);
+run_log [ $klist, '-f' ] or BAIL_OUT($?);
test_access(
$node,
@@ -275,34 +293,56 @@ $node->restart;
test_access(
$node,
'test1',
- 'SELECT gss_authenticated AND encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
+ 'SELECT gss_authenticated AND encrypted AND NOT credentials_proxied FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
0,
'',
- 'succeeds with mapping with default gssencmode and host hba',
+ 'succeeds with mapping with default gssencmode and host hba, ticket not forwardable',
"connection authenticated: identity=\"test1\@$realm\" method=gss",
- "connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, principal=test1\@$realm)"
+ "connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, proxy_credentials=no, principal=test1\@$realm)"
);
test_access(
$node,
'test1',
- 'SELECT gss_authenticated AND encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
+ 'SELECT gss_authenticated AND encrypted AND NOT credentials_proxied FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
0,
'gssencmode=prefer',
- 'succeeds with GSS-encrypted access preferred with host hba',
+ 'succeeds with GSS-encrypted access preferred with host hba, ticket not forwardable',
"connection authenticated: identity=\"test1\@$realm\" method=gss",
- "connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, principal=test1\@$realm)"
+ "connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, proxy_credentials=no, principal=test1\@$realm)"
);
test_access(
$node,
'test1',
- 'SELECT gss_authenticated AND encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
+ 'SELECT gss_authenticated AND encrypted AND NOT credentials_proxied FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
0,
'gssencmode=require',
- 'succeeds with GSS-encrypted access required with host hba',
+ 'succeeds with GSS-encrypted access required with host hba, ticket not forwardable',
+ "connection authenticated: identity=\"test1\@$realm\" method=gss",
+ "connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, proxy_credentials=no, principal=test1\@$realm)"
+);
+
+test_access(
+ $node,
+ 'test1',
+ 'SELECT gss_authenticated AND encrypted AND NOT credentials_proxied FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
+ 0,
+ 'gssencmode=prefer gssdeleg=enable',
+ 'succeeds with GSS-encrypted access preferred with host hba and credentials not delegated even though asked for (ticket not forwardable)',
"connection authenticated: identity=\"test1\@$realm\" method=gss",
- "connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, principal=test1\@$realm)"
+ "connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, proxy_credentials=no, principal=test1\@$realm)"
);
+test_access(
+ $node,
+ 'test1',
+ 'SELECT gss_authenticated AND encrypted AND NOT credentials_proxied FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
+ 0,
+ 'gssencmode=require gssdeleg=enable',
+ 'succeeds with GSS-encrypted access required with host hba and credentials not delegated even though asked for (ticket not forwardable)',
+ "connection authenticated: identity=\"test1\@$realm\" method=gss",
+ "connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, proxy_credentials=no, principal=test1\@$realm)"
+);
+
# Test that we can transport a reasonable amount of data.
test_query(
@@ -345,27 +385,101 @@ test_query(
unlink($node->data_dir . '/pg_hba.conf');
$node->append_conf('pg_hba.conf',
qq{hostgssenc all all $hostaddr/32 gss map=mymap});
+
+string_replace_file($krb5_conf, "forwardable = false", "forwardable = true");
+
+run_log [ $kinit, 'test1' ], \$test1_password or BAIL_OUT($?);
+run_log [ $klist, '-f' ] or BAIL_OUT($?);
+
+test_access(
+ $node,
+ 'test1',
+ 'SELECT gss_authenticated AND encrypted AND NOT credentials_proxied from pg_stat_gssapi where pid = pg_backend_pid();',
+ 0,
+ 'gssencmode=prefer gssdeleg=enable',
+ 'succeeds with GSS-encrypted access preferred and hostgssenc hba and credentials not forwarded (server does not accept them, default)',
+ "connection authenticated: identity=\"test1\@$realm\" method=gss",
+ "connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, proxy_credentials=no, principal=test1\@$realm)"
+);
+test_access(
+ $node,
+ 'test1',
+ 'SELECT gss_authenticated AND encrypted AND NOT credentials_proxied from pg_stat_gssapi where pid = pg_backend_pid();',
+ 0,
+ 'gssencmode=require gssdeleg=enable',
+ 'succeeds with GSS-encrypted access required and hostgssenc hba and credentials not forwarded (server does not accept them, default)',
+ "connection authenticated: identity=\"test1\@$realm\" method=gss",
+ "connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, proxy_credentials=no, principal=test1\@$realm)"
+);
+
+$node->append_conf('postgresql.conf',
+ qq{gss_accept_deleg=off});
+$node->restart;
+
+test_access(
+ $node,
+ 'test1',
+ 'SELECT gss_authenticated AND encrypted AND NOT credentials_proxied from pg_stat_gssapi where pid = pg_backend_pid();',
+ 0,
+ 'gssencmode=prefer gssdeleg=enable',
+ 'succeeds with GSS-encrypted access preferred and hostgssenc hba and credentials not forwarded (server does not accept them, explicitly disabled)',
+ "connection authenticated: identity=\"test1\@$realm\" method=gss",
+ "connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, proxy_credentials=no, principal=test1\@$realm)"
+);
+test_access(
+ $node,
+ 'test1',
+ 'SELECT gss_authenticated AND encrypted AND NOT credentials_proxied from pg_stat_gssapi where pid = pg_backend_pid();',
+ 0,
+ 'gssencmode=require gssdeleg=enable',
+ 'succeeds with GSS-encrypted access required and hostgssenc hba and credentials not forwarded (server does not accept them, explicitly disabled)',
+ "connection authenticated: identity=\"test1\@$realm\" method=gss",
+ "connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, proxy_credentials=no, principal=test1\@$realm)"
+);
+
+$node->append_conf('postgresql.conf',
+ qq{gss_accept_deleg=on});
$node->restart;
test_access(
$node,
'test1',
- 'SELECT gss_authenticated AND encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
+ 'SELECT gss_authenticated AND encrypted AND credentials_proxied from pg_stat_gssapi where pid = pg_backend_pid();',
+ 0,
+ 'gssencmode=prefer gssdeleg=enable',
+ 'succeeds with GSS-encrypted access preferred and hostgssenc hba and credentials forwarded',
+ "connection authenticated: identity=\"test1\@$realm\" method=gss",
+ "connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, proxy_credentials=yes, principal=test1\@$realm)"
+);
+test_access(
+ $node,
+ 'test1',
+ 'SELECT gss_authenticated AND encrypted AND credentials_proxied from pg_stat_gssapi where pid = pg_backend_pid();',
+ 0,
+ 'gssencmode=require gssdeleg=enable',
+ 'succeeds with GSS-encrypted access required and hostgssenc hba and credentials forwarded',
+ "connection authenticated: identity=\"test1\@$realm\" method=gss",
+ "connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, proxy_credentials=yes, principal=test1\@$realm)"
+);
+test_access(
+ $node,
+ 'test1',
+ 'SELECT gss_authenticated AND encrypted AND NOT credentials_proxied FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
0,
'gssencmode=prefer',
- 'succeeds with GSS-encrypted access preferred and hostgssenc hba',
+ 'succeeds with GSS-encrypted access preferred and hostgssenc hba and credentials not forwarded',
"connection authenticated: identity=\"test1\@$realm\" method=gss",
- "connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, principal=test1\@$realm)"
+ "connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, proxy_credentials=no, principal=test1\@$realm)"
);
test_access(
$node,
'test1',
- 'SELECT gss_authenticated AND encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
+ 'SELECT gss_authenticated AND encrypted AND NOT credentials_proxied FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
0,
- 'gssencmode=require',
- 'succeeds with GSS-encrypted access required and hostgssenc hba',
+ 'gssencmode=require gssdeleg=disable',
+ 'succeeds with GSS-encrypted access required and hostgssenc hba and credentials not forwarded',
"connection authenticated: identity=\"test1\@$realm\" method=gss",
- "connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, principal=test1\@$realm)"
+ "connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, proxy_credentials=no, principal=test1\@$realm)"
);
test_access($node, 'test1', 'SELECT true', 2, 'gssencmode=disable',
'fails with GSS encryption disabled and hostgssenc hba');
@@ -378,26 +492,42 @@ $node->restart;
test_access(
$node,
'test1',
- 'SELECT gss_authenticated and not encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
+ 'SELECT gss_authenticated AND NOT encrypted AND credentials_proxied FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
0,
- 'gssencmode=prefer',
+ 'gssencmode=prefer gssdeleg=enable',
'succeeds with GSS-encrypted access preferred and hostnogssenc hba, but no encryption',
"connection authenticated: identity=\"test1\@$realm\" method=gss",
- "connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=no, principal=test1\@$realm)"
+ "connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=no, proxy_credentials=yes, principal=test1\@$realm)"
);
test_access($node, 'test1', 'SELECT true', 2, 'gssencmode=require',
'fails with GSS-encrypted access required and hostnogssenc hba');
test_access(
$node,
'test1',
- 'SELECT gss_authenticated and not encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
+ 'SELECT gss_authenticated AND NOT encrypted AND credentials_proxied FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
0,
- 'gssencmode=disable',
+ 'gssencmode=disable gssdeleg=enable',
'succeeds with GSS encryption disabled and hostnogssenc hba',
"connection authenticated: identity=\"test1\@$realm\" method=gss",
- "connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=no, principal=test1\@$realm)"
+ "connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=no, proxy_credentials=yes, principal=test1\@$realm)"
);
+test_query(
+ $node,
+ 'test1',
+ "SELECT * FROM dblink('user=test1 dbname=$dbname host=$host hostaddr=$hostaddr port=$port password=1234','select 1') as t1(c1 int);",
+ qr/^1$/s,
+ 'gssencmode=prefer gssdeleg=enable',
+ 'dblink works not-encrypted');
+
+test_query(
+ $node,
+ 'test1',
+ "TABLE tf1;",
+ qr/^1$/s,
+ 'gssencmode=prefer gssdeleg=enable',
+ 'postgres_fdw works not-encrypted');
+
truncate($node->data_dir . '/pg_ident.conf', 0);
unlink($node->data_dir . '/pg_hba.conf');
$node->append_conf('pg_hba.conf',
@@ -407,14 +537,30 @@ $node->restart;
test_access(
$node,
'test1',
- 'SELECT gss_authenticated AND encrypted from pg_stat_gssapi where pid = pg_backend_pid();',
+ 'SELECT gss_authenticated AND encrypted AND credentials_proxied FROM pg_stat_gssapi WHERE pid = pg_backend_pid();',
0,
- '',
+ 'gssdeleg=enable',
'succeeds with include_realm=0 and defaults',
"connection authenticated: identity=\"test1\@$realm\" method=gss",
- "connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, principal=test1\@$realm)"
+ "connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, proxy_credentials=yes, principal=test1\@$realm)"
);
+test_query(
+ $node,
+ 'test1',
+ "SELECT * FROM dblink('user=test1 dbname=$dbname host=$host hostaddr=$hostaddr port=$port password=1234','select 1') as t1(c1 int);",
+ qr/^1$/s,
+ 'gssencmode=require gssdeleg=enable',
+ 'dblink works encrypted');
+
+test_query(
+ $node,
+ 'test1',
+ "TABLE tf1;",
+ qr/^1$/s,
+ 'gssencmode=require gssdeleg=enable',
+ 'postgres_fdw works encrypted');
+
# Reset pg_hba.conf, and cause a usermap failure with an authentication
# that has passed.
unlink($node->data_dir . '/pg_hba.conf');
diff --git a/src/test/perl/PostgreSQL/Test/Utils.pm b/src/test/perl/PostgreSQL/Test/Utils.pm
index 878e12b15e..9249954b49 100644
--- a/src/test/perl/PostgreSQL/Test/Utils.pm
+++ b/src/test/perl/PostgreSQL/Test/Utils.pm
@@ -65,6 +65,7 @@ our @EXPORT = qw(
slurp_dir
slurp_file
append_to_file
+ string_replace_file
check_mode_recursive
chmod_recursive
check_pg_config
@@ -549,6 +550,32 @@ sub append_to_file
=pod
+=item string_replace_file(filename, find, replace)
+
+Find and replace string of a given file.
+
+=cut
+
+sub string_replace_file
+{
+ my ($filename, $find, $replace) = @_;
+ open(my $in, '<', $filename);
+ my $content;
+ while(<$in>)
+ {
+ $_ =~ s/$find/$replace/;
+ $content = $content.$_;
+ }
+ close $in;
+ open(my $out, '>', $filename);
+ print $out $content;
+ close($out);
+
+ return;
+}
+
+=pod
+
=item check_mode_recursive(dir, expected_dir_mode, expected_file_mode, ignore_list)
Check that all file/dir modes in a directory match the expected values,
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 174b725fff..9596a673ad 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1759,7 +1759,7 @@ pg_stat_activity| SELECT s.datid,
s.query_id,
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, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id)
+ 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, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, gss_proxy, leader_pid, query_id)
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,
@@ -1873,8 +1873,9 @@ pg_stat_database_conflicts| SELECT oid AS datid,
pg_stat_gssapi| SELECT pid,
gss_auth AS gss_authenticated,
gss_princ AS principal,
- 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, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id)
+ gss_enc AS encrypted,
+ gss_proxy AS credentials_proxied
+ 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, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, gss_proxy, leader_pid, query_id)
WHERE (client_port IS NOT NULL);
pg_stat_io| SELECT backend_type,
io_object,
@@ -2067,7 +2068,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, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id)
+ 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, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, gss_proxy, leader_pid, query_id)
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_replication_slots| SELECT s.slot_name,
@@ -2101,7 +2102,7 @@ pg_stat_ssl| SELECT pid,
ssl_client_dn AS client_dn,
ssl_client_serial AS client_serial,
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, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, leader_pid, query_id)
+ 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, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, gss_enc, gss_proxy, leader_pid, query_id)
WHERE (client_port IS NOT NULL);
pg_stat_subscription| SELECT su.oid AS subid,
su.subname,