diff options
Diffstat (limited to 'src')
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, |