aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/catalog/system_views.sql3
-rw-r--r--src/backend/foreign/foreign.c1
-rw-r--r--src/backend/libpq/auth.c13
-rw-r--r--src/backend/libpq/be-gssapi-common.c53
-rw-r--r--src/backend/libpq/be-secure-gssapi.c26
-rw-r--r--src/backend/utils/activity/backend_status.c1
-rw-r--r--src/backend/utils/adt/pgstatfuncs.c21
-rw-r--r--src/backend/utils/init/postinit.c8
-rw-r--r--src/backend/utils/misc/guc_tables.c10
-rw-r--r--src/backend/utils/misc/postgresql.conf.sample1
-rw-r--r--src/include/catalog/pg_proc.dat6
-rw-r--r--src/include/libpq/auth.h1
-rw-r--r--src/include/libpq/be-gssapi-common.h3
-rw-r--r--src/include/libpq/libpq-be.h2
-rw-r--r--src/include/utils/backend_status.h1
-rw-r--r--src/interfaces/libpq/exports.txt1
-rw-r--r--src/interfaces/libpq/fe-auth.c15
-rw-r--r--src/interfaces/libpq/fe-connect.c17
-rw-r--r--src/interfaces/libpq/fe-secure-gssapi.c23
-rw-r--r--src/interfaces/libpq/libpq-fe.h1
-rw-r--r--src/interfaces/libpq/libpq-int.h2
-rw-r--r--src/test/kerberos/Makefile3
-rw-r--r--src/test/kerberos/t/001_auth.pl331
-rw-r--r--src/test/perl/PostgreSQL/Test/Utils.pm27
-rw-r--r--src/test/regress/expected/rules.out11
25 files changed, 523 insertions, 58 deletions
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 701c340fc49..2129c916aa1 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -979,7 +979,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_deleg AS credentials_delegated
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 dca02271dc9..6e1977fa620 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 bc0cf26b122..00ec9da284b 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -165,6 +165,7 @@ static int CheckCertAuth(Port *port);
*/
char *pg_krb_server_keyfile;
bool pg_krb_caseins_users;
+bool pg_gss_accept_deleg;
/*----------------------------------------------------------------
@@ -918,6 +919,7 @@ pg_GSS_recvauth(Port *port)
int mtype;
StringInfoData buf;
gss_buffer_desc gbuf;
+ gss_cred_id_t delegated_creds;
/*
* Use the configured keytab, if there is one. Unfortunately, Heimdal
@@ -947,6 +949,9 @@ pg_GSS_recvauth(Port *port)
*/
port->gss->ctx = GSS_C_NO_CONTEXT;
+ delegated_creds = GSS_C_NO_CREDENTIAL;
+ port->gss->delegated_creds = false;
+
/*
* Loop through GSSAPI message exchange. This exchange can consist of
* multiple messages sent in both directions. First message is always from
@@ -997,7 +1002,7 @@ pg_GSS_recvauth(Port *port)
&port->gss->outbuf,
&gflags,
NULL,
- NULL);
+ pg_gss_accept_deleg ? &delegated_creds : NULL);
/* gbuf no longer used */
pfree(buf.data);
@@ -1009,6 +1014,12 @@ pg_GSS_recvauth(Port *port)
CHECK_FOR_INTERRUPTS();
+ if (delegated_creds != GSS_C_NO_CREDENTIAL && gflags & GSS_C_DELEG_FLAG)
+ {
+ pg_store_delegated_credential(delegated_creds);
+ port->gss->delegated_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 fb39c760d8c..64d41e52915 100644
--- a/src/backend/libpq/be-gssapi-common.c
+++ b/src/backend/libpq/be-gssapi-common.c
@@ -92,3 +92,56 @@ 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_delegated_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 delegated 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 delegated 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 3b55f431999..73f8ce85549 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 delegated_creds;
/*
* 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));
+ delegated_creds = GSS_C_NO_CREDENTIAL;
+ port->gss->delegated_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 ? &delegated_creds : 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 (delegated_creds != GSS_C_NO_CREDENTIAL)
+ {
+ pg_store_delegated_credential(delegated_creds);
+ port->gss->delegated_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 credentials were included on this
+ * connection.
+ */
+bool
+be_gssapi_get_deleg(Port *port)
+{
+ if (!port || !port->gss)
+ return NULL;
+
+ return port->gss->delegated_creds;
+}
diff --git a/src/backend/utils/activity/backend_status.c b/src/backend/utils/activity/backend_status.c
index 608d01ea0dd..391d5de0435 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_deleg = be_gssapi_get_deleg(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 ae180da4d0e..e79b065d214 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,8 @@ 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_deleg); /* GSS credentials
+ * delegated */
}
else
{
@@ -607,11 +609,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
+ * delegated */
}
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 +644,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 60feae0f1b1..5af87a78683 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, deleg_credentials=%s, principal=%s)"),
be_gssapi_get_auth(port) ? _("yes") : _("no"),
be_gssapi_get_enc(port) ? _("yes") : _("no"),
+ be_gssapi_get_deleg(port) ? _("yes") : _("no"),
princ);
else
appendStringInfo(&logmsg,
- _(" GSS (authenticated=%s, encrypted=%s)"),
+ _(" GSS (authenticated=%s, encrypted=%s, deleg_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_deleg(port) ? _("yes") : _("no"));
}
#endif
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 1067537e74c..cab3ddbe11e 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -1728,6 +1728,16 @@ struct config_bool ConfigureNamesBool[] =
},
{
+ {"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."),
NULL
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index e715aff3b81..dce5049bc24 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -101,6 +101,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 dcbc4c2eb5d..b516cee8bd8 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -5438,9 +5438,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_deleg,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 9916c99df17..e4d0e38c1ee 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 facd24ff7f9..0381f0ce771 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_delegated_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 ac6407e9f61..e9df4295e20 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 delegated_creds; /* GSSAPI Delegated 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_deleg(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 f7bd83113ac..9651cb1d0c0 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_deleg; /* If credentials delegated */
} PgBackendGSSStatus;
diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt
index e8bcc883709..7ded77aff37 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 b0550e63324..fe2634230a3 100644
--- a/src/interfaces/libpq/fe-auth.c
+++ b/src/interfaces/libpq/fe-auth.c
@@ -58,7 +58,8 @@ 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;
@@ -92,12 +93,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(&conn->gcred))
+ conn->gcred = 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,
+ conn->gcred,
&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,
@@ -139,6 +147,7 @@ pg_GSS_continue(PGconn *conn, int payloadlen)
{
conn->client_finished_auth = true;
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 40fef0e2c81..fcd3d0d9a35 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -343,6 +343,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)},
@@ -617,6 +621,7 @@ pqDropServerData(PGconn *conn)
conn->auth_req_received = false;
conn->client_finished_auth = false;
conn->password_needed = false;
+ conn->gssapi_used = false;
conn->write_failed = false;
free(conn->write_err_msg);
conn->write_err_msg = NULL;
@@ -4448,6 +4453,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);
@@ -7313,6 +7319,17 @@ PQconnectionUsedPassword(const PGconn *conn)
}
int
+PQconnectionUsedGSSAPI(const PGconn *conn)
+{
+ if (!conn)
+ return false;
+ if (conn->gssapi_used)
+ return true;
+ else
+ return false;
+}
+
+int
PQclientEncoding(const PGconn *conn)
{
if (!conn || conn->status != CONNECTION_OK)
diff --git a/src/interfaces/libpq/fe-secure-gssapi.c b/src/interfaces/libpq/fe-secure-gssapi.c
index 038e847b7e9..bf87ae3fd1a 100644
--- a/src/interfaces/libpq/fe-secure-gssapi.c
+++ b/src/interfaces/libpq/fe-secure-gssapi.c
@@ -477,7 +477,8 @@ 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,
@@ -621,13 +622,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 (conn->gcred == GSS_C_NO_CREDENTIAL)
+ (void) pg_GSS_have_cred_cache(&conn->gcred);
+
+ /*
+ * 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 (conn->gcred != 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,
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 +665,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 f3d92204964..7476dbe0e90 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 d93e976ca57..ce0167c1b66 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -404,6 +404,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 */
@@ -465,6 +466,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 7765f3f93b1..f460d2c0e70 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 458246b4d71..bf12752529a 100644
--- a/src/test/kerberos/t/001_auth.pl
+++ b/src/test/kerberos/t/001_auth.pl
@@ -7,6 +7,9 @@
# that the server-side pg_stat_gssapi view reports what we expect to
# see for each test and that SYSTEM_USER returns what we expect to see.
#
+# Also test that GSSAPI delegation is working properly and that those
+# credentials can be used to make dblink / postgres_fdw connections.
+#
# Since this requires setting up a full KDC, it doesn't make much sense
# to have multiple test scripts (since they'd have to also create their
# own KDC and that could cause race conditions or other problems)- so
@@ -56,6 +59,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 +68,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)
{
@@ -86,6 +91,8 @@ my $kdc_datadir = "${PostgreSQL::Test::Utils::tmp_check}/krb5kdc";
my $kdc_pidfile = "${PostgreSQL::Test::Utils::tmp_check}/krb5kdc.pid";
my $keytab = "${PostgreSQL::Test::Utils::tmp_check}/krb5.keytab";
+my $pgpass = "${PostgreSQL::Test::Utils::tmp_check}/.pgpass";
+
my $dbname = 'postgres';
my $username = 'test1';
my $application = '001_auth.pl';
@@ -100,6 +107,14 @@ $stdout =~ m/Kerberos 5 release ([0-9]+\.[0-9]+)/
or BAIL_OUT("could not get Kerberos version");
$krb5_version = $1;
+# Construct a pgpass file to make sure we don't use it
+append_to_file(
+ $pgpass,
+ '*:*:*:*:abc123'
+);
+
+chmod 0600, $pgpass;
+
# Build the krb5.conf to use.
#
# Explicitly specify the default (test) realm and the KDC for
@@ -126,12 +141,14 @@ kdc = FILE:$kdc_log
dns_lookup_realm = false
dns_lookup_kdc = false
default_realm = $realm
+forwardable = false
rdns = false
[realms]
$realm = {
kdc = $hostaddr:$kdc_port
-}!);
+}
+!);
append_to_file(
$kdc_conf,
@@ -204,7 +221,28 @@ lc_messages = 'C'
});
$node->start;
+my $port = $node->port();
+
$node->safe_psql('postgres', 'CREATE USER test1;');
+$node->safe_psql('postgres', "CREATE USER test2 WITH ENCRYPTED PASSWORD 'abc123';");
+$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', "CREATE SERVER s2 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (port '$port', dbname 'postgres', passfile '$pgpass');");
+
+$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');");
+$node->safe_psql('postgres', "CREATE USER MAPPING FOR test1 SERVER s2 OPTIONS (user 'test2');");
+
+$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;");
+
+$node->safe_psql('postgres', "CREATE FOREIGN TABLE tf2 (c1 int) SERVER s2 OPTIONS (schema_name 'public', table_name 't1');");
+$node->safe_psql('postgres', "GRANT SELECT ON tf2 TO test1;");
# Set up a table for SYSTEM_USER parallel worker testing.
$node->safe_psql('postgres',
@@ -271,12 +309,16 @@ sub test_query
unlink($node->data_dir . '/pg_hba.conf');
$node->append_conf('pg_hba.conf',
- qq{host all all $hostaddr/32 gss map=mymap});
+ qq{
+local all test2 scram-sha-256
+host all all $hostaddr/32 gss map=mymap
+});
$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,
@@ -294,35 +336,58 @@ $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_delegated 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, deleg_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_delegated 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, deleg_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_delegated 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, principal=test1\@$realm)"
+ "connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, deleg_credentials=no, principal=test1\@$realm)"
);
+test_access(
+ $node,
+ 'test1',
+ 'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated 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, deleg_credentials=no, principal=test1\@$realm)"
+);
+test_access(
+ $node,
+ 'test1',
+ 'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated 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, deleg_credentials=no, principal=test1\@$realm)"
+);
+
+
# Test that we can transport a reasonable amount of data.
test_query(
$node,
@@ -389,29 +454,164 @@ test_query(
unlink($node->data_dir . '/pg_hba.conf');
$node->append_conf('pg_hba.conf',
- qq{hostgssenc all all $hostaddr/32 gss map=mymap});
+ qq{
+ local all test2 scram-sha-256
+ 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_delegated 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, deleg_credentials=no, principal=test1\@$realm)"
+);
+test_access(
+ $node,
+ 'test1',
+ 'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated 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, deleg_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_delegated 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, deleg_credentials=no, principal=test1\@$realm)"
+);
+test_access(
+ $node,
+ 'test1',
+ 'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated 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, deleg_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_delegated 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, deleg_credentials=yes, principal=test1\@$realm)"
+);
+test_access(
+ $node,
+ 'test1',
+ 'SELECT gss_authenticated AND encrypted AND credentials_delegated 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, deleg_credentials=yes, principal=test1\@$realm)"
+);
+test_access(
+ $node,
+ 'test1',
+ 'SELECT gss_authenticated AND encrypted AND NOT credentials_delegated 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, deleg_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_delegated 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 explicitly 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, deleg_credentials=no, principal=test1\@$realm)"
+);
+
+my $psql_out = '';
+my $psql_stderr = '';
+my $psql_rc = '';
+
+$psql_rc = $node->psql(
+ 'postgres',
+ "SELECT * FROM dblink('user=test1 dbname=$dbname host=$host hostaddr=$hostaddr port=$port','select 1') as t1(c1 int);",
+ connstr => "user=test1 host=$host hostaddr=$hostaddr gssencmode=require gssdeleg=disable",
+ stdout => \$psql_out,
+ stderr => \$psql_stderr
+);
+is($psql_rc,'3','dblink attempt fails without delegated credentials');
+like($psql_stderr, qr/password or GSSAPI delegated credentials required/,'dblink does not work without delegated credentials');
+like($psql_out, qr/^$/,'dblink does not work without delegated credentials');
+
+$psql_out = '';
+$psql_stderr = '';
+
+$psql_rc = $node->psql(
+ 'postgres',
+ "SELECT * FROM dblink('user=test2 dbname=$dbname port=$port passfile=$pgpass','select 1') as t1(c1 int);",
+ connstr => "user=test1 host=$host hostaddr=$hostaddr gssencmode=require gssdeleg=disable",
+ stdout => \$psql_out,
+ stderr => \$psql_stderr
);
+is($psql_rc,'3','dblink does not work without delegated credentials and with passfile');
+like($psql_stderr, qr/password or GSSAPI delegated credentials required/,'dblink does not work without delegated credentials and with passfile');
+like($psql_out, qr/^$/,'dblink does not work without delegated credentials and with passfile');
+
+$psql_out = '';
+$psql_stderr = '';
+
+$psql_rc = $node->psql(
+ 'postgres',
+ "TABLE tf1;",
+ connstr => "user=test1 host=$host hostaddr=$hostaddr gssencmode=require gssdeleg=disable",
+ stdout => \$psql_out,
+ stderr => \$psql_stderr
+);
+is($psql_rc,'3','postgres_fdw does not work without delegated credentials');
+like($psql_stderr, qr/password or GSSAPI delegated credentials required/,'postgres_fdw does not work without delegated credentials');
+like($psql_out, qr/^$/,'postgres_fdw does not work without delegated credentials');
+
+$psql_out = '';
+$psql_stderr = '';
+
+$psql_rc = $node->psql(
+ 'postgres',
+ "TABLE tf2;",
+ connstr => "user=test1 host=$host hostaddr=$hostaddr gssencmode=require gssdeleg=disable",
+ stdout => \$psql_out,
+ stderr => \$psql_stderr
+);
+is($psql_rc,'3','postgres_fdw does not work without delegated credentials and with passfile');
+like($psql_stderr, qr/password or GSSAPI delegated credentials required/,'postgres_fdw does not work without delegated credentials and with passfile');
+like($psql_out, qr/^$/,'postgres_fdw does not work without delegated credentials and with passfile');
+
test_access($node, 'test1', 'SELECT true', 2, 'gssencmode=disable',
'fails with GSS encryption disabled and hostgssenc hba');
@@ -427,54 +627,123 @@ $node->connect_ok(
unlink($node->data_dir . '/pg_hba.conf');
$node->append_conf('pg_hba.conf',
- qq{hostnogssenc all all $hostaddr/32 gss map=mymap});
+ qq{
+ local all test2 scram-sha-256
+ hostnogssenc all all $hostaddr/32 gss map=mymap
+});
$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_delegated 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, deleg_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_delegated 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, deleg_credentials=yes, principal=test1\@$realm)"
+);
+
+test_query(
+ $node,
+ 'test1',
+ "SELECT * FROM dblink('user=test1 dbname=$dbname host=$host hostaddr=$hostaddr port=$port','select 1') as t1(c1 int);",
+ qr/^1$/s,
+ 'gssencmode=prefer gssdeleg=enable',
+ 'dblink works not-encrypted (server not configured to accept encrypted GSSAPI connections)');
+
+test_query(
+ $node,
+ 'test1',
+ "TABLE tf1;",
+ qr/^1$/s,
+ 'gssencmode=prefer gssdeleg=enable',
+ 'postgres_fdw works not-encrypted (server not configured to accept encrypted GSSAPI connections)');
+
+$psql_out = '';
+$psql_stderr = '';
+
+$psql_rc = $node->psql(
+ 'postgres',
+ "SELECT * FROM dblink('user=test2 dbname=$dbname port=$port passfile=$pgpass','select 1') as t1(c1 int);",
+ connstr => "user=test1 host=$host hostaddr=$hostaddr gssencmode=prefer gssdeleg=enable",
+ stdout => \$psql_out,
+ stderr => \$psql_stderr
+);
+is($psql_rc,'3','dblink does not work with delegated credentials and with passfile');
+like($psql_stderr, qr/password or GSSAPI delegated credentials required/,'dblink does not work with delegated credentials and with passfile');
+like($psql_out, qr/^$/,'dblink does not work with delegated credentials and with passfile');
+
+$psql_out = '';
+$psql_stderr = '';
+
+$psql_rc = $node->psql(
+ 'postgres',
+ "TABLE tf2;",
+ connstr => "user=test1 host=$host hostaddr=$hostaddr gssencmode=prefer gssdeleg=enable",
+ stdout => \$psql_out,
+ stderr => \$psql_stderr
);
+is($psql_rc,'3','postgres_fdw does not work with delegated credentials and with passfile');
+like($psql_stderr, qr/password or GSSAPI delegated credentials required/,'postgres_fdw does not work with delegated credentials and with passfile');
+like($psql_out, qr/^$/,'postgres_fdw does not work with delegated credentials and with passfile');
truncate($node->data_dir . '/pg_ident.conf', 0);
unlink($node->data_dir . '/pg_hba.conf');
$node->append_conf('pg_hba.conf',
- qq{host all all $hostaddr/32 gss include_realm=0});
+ qq{
+ local all test2 scram-sha-256
+ host all all $hostaddr/32 gss include_realm=0
+});
$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_delegated 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, deleg_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');
$node->append_conf('pg_hba.conf',
- qq{host all all $hostaddr/32 gss include_realm=0 krb_realm=EXAMPLE.ORG});
+ qq{
+ local all test2 scram-sha-256
+ host all all $hostaddr/32 gss include_realm=0 krb_realm=EXAMPLE.ORG
+});
$node->restart;
test_access(
diff --git a/src/test/perl/PostgreSQL/Test/Utils.pm b/src/test/perl/PostgreSQL/Test/Utils.pm
index 878e12b15ed..9249954b499 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 2d75dd66567..919d947ec0f 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1760,7 +1760,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_deleg, 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,
@@ -1876,8 +1876,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_deleg AS credentials_delegated
+ 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_deleg, leader_pid, query_id)
WHERE (client_port IS NOT NULL);
pg_stat_io| SELECT backend_type,
io_object,
@@ -2075,7 +2076,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_deleg, 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,
@@ -2109,7 +2110,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_deleg, leader_pid, query_id)
WHERE (client_port IS NOT NULL);
pg_stat_subscription| SELECT su.oid AS subid,
su.subname,