Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1pSx1o-0003QN-40 for pgsql-hackers@arkaria.postgresql.org; Fri, 17 Feb 2023 09:27:40 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1pSx1m-0000aE-Rr for pgsql-hackers@arkaria.postgresql.org; Fri, 17 Feb 2023 09:27:38 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1pSx1m-0000a5-Cz for pgsql-hackers@lists.postgresql.org; Fri, 17 Feb 2023 09:27:38 +0000 Received: from tamriel.snowman.net ([70.109.60.50]) by magus.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1pSx1e-0003jF-EC for pgsql-hackers@lists.postgresql.org; Fri, 17 Feb 2023 09:27:37 +0000 Received: by tamriel.snowman.net (Postfix, from userid 1000) id C42995F7BC; Fri, 17 Feb 2023 04:27:28 -0500 (EST) Date: Fri, 17 Feb 2023 04:27:28 -0500 From: Stephen Frost To: Michael Paquier Cc: Jacob Champion , pgsql-hackers , Magnus Hagander , Peter Eisentraut , Tom Lane , Robert Haas Subject: Re: Kerberos delegation support in libpq and postgres_fdw Message-ID: References: <20220406192703.GY10577@tamriel.snowman.net> <20220408042126.GC10577@tamriel.snowman.net> <20220408122130.GD10577@tamriel.snowman.net> <23337c51-7a48-d5a8-569d-ef3ce6fe235f@timescale.com> <78ea2273-f370-5a78-903a-886511e2511c@timescale.com> MIME-Version: 1.0 Content-Type: multipart/signed; micalg=pgp-sha512; protocol="application/pgp-signature"; boundary="XvWZoWEx23JIRy4T" Content-Disposition: inline In-Reply-To: User-Agent: Mutt/2.1.4 (2021-12-11) List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --XvWZoWEx23JIRy4T Content-Type: multipart/mixed; boundary="yTvo40KjU/RXUJtp" Content-Disposition: inline --yTvo40KjU/RXUJtp Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Content-Transfer-Encoding: quoted-printable Greetings, * Michael Paquier (michael@paquier.xyz) wrote: > On Mon, Sep 19, 2022 at 02:05:39PM -0700, Jacob Champion wrote: > > It's not prevented, because a password is being used. In my tests I'm > > connecting as an unprivileged user. > >=20 > > You're claiming that the middlebox shouldn't be doing this. If this new > > default behavior were the historical behavior, then I would have agreed. > > But the cat's already out of the bag on that, right? It's safe today. > > And if it's not safe today for some other reason, please share why, and > > maybe I can work on a patch to try to prevent people from doing it. >=20 > Please note that this has been marked as returned with feedback in the > current CF, as this has remained unanswered for a bit more than three > weeks. There's some ongoing discussion about how to handle outbound connections =66rom the server ending up picking up credentials from the server's environment (that really shouldn't be allowed unless specifically asked for..), that's ultimately an independent change from what this patch is doing. Here's an updated version which does address Robert's concerns around having this disabled by default and having options on both the server and client side saying if it is to be enabled or not. Also added to pg_stat_gssapi a field that indicates if credentials were proxied or not and made some other improvements and added additional regression tests to test out various combinations. Thanks, Stephen --yTvo40KjU/RXUJtp Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="gss_delegation_v2.patch" Content-Transfer-Encoding: quoted-printable diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c index 78a8bcee6e..713ef7c248 100644 --- a/contrib/dblink/dblink.c +++ b/contrib/dblink/dblink.c @@ -2589,7 +2589,7 @@ dblink_security_check(PGconn *conn, remoteConn *rconn) { if (!superuser()) { - if (!PQconnectionUsedPassword(conn)) + if (!(PQconnectionUsedPassword(conn) || PQconnectionUsedGSSAPI(conn))) { libpqsrv_disconnect(conn); if (rconn) @@ -2597,8 +2597,8 @@ dblink_security_check(PGconn *conn, remoteConn *rconn) =20 ereport(ERROR, (errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED), - errmsg("password is required"), - errdetail("Non-superuser cannot connect if the server does not reque= st a password."), + errmsg("password or GSSAPI is required"), + errdetail("Non-superuser cannot connect if the server does not reque= st a password or use GSSAPI."), errhint("Target server's authentication method must be changed."))); } } diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postg= res_fdw/expected/postgres_fdw.out index d5fc61446a..77656ccf80 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -171,7 +171,8 @@ ALTER SERVER testserver1 OPTIONS ( sslcrl 'value', --requirepeer 'value', krbsrvname 'value', - gsslib 'value' + gsslib 'value', + gssdeleg 'value' --replication 'value' ); -- Error, invalid list syntax diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c index 984e4d168a..bb6f309907 100644 --- a/contrib/postgres_fdw/option.c +++ b/contrib/postgres_fdw/option.c @@ -286,6 +286,9 @@ InitPgFdwOptions(void) {"sslcert", UserMappingRelationId, true}, {"sslkey", UserMappingRelationId, true}, =20 + /* gssencmode is also libpq option, same to above. */ + {"gssencmode", UserMappingRelationId, true}, + {NULL, InvalidOid, false} }; =20 diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_f= dw/sql/postgres_fdw.sql index 1e50be137b..1a43bdf55b 100644 --- a/contrib/postgres_fdw/sql/postgres_fdw.sql +++ b/contrib/postgres_fdw/sql/postgres_fdw.sql @@ -185,7 +185,8 @@ ALTER SERVER testserver1 OPTIONS ( sslcrl 'value', --requirepeer 'value', krbsrvname 'value', - gsslib 'value' + gsslib 'value', + gssdeleg 'value' --replication 'value' ); =20 diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index ecd9aa73ef..a931996968 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -1170,6 +1170,23 @@ include_dir 'conf.d' =20 + + gss_accept_deleg (boolean) + + gss_accept_deleg configuration paramete= r + + + + + Sets whether GSSAPI delegation should be accepted from the client. + The default is off meaning credentials from the= client will + NOT be accepted. Changing this to on will make= the server + accept credentials delegated to it from the client. This parameter= can only be + set in the postgresql.conf file or on the ser= ver command line. + + + + db_user_namespace (boolean) diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index 0e7ae70c70..9f3e12b2a7 100644 --- a/doc/src/sgml/libpq.sgml +++ b/doc/src/sgml/libpq.sgml @@ -1879,6 +1879,18 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname =20 + + gssdeleg + + + Forward (delegate) GSS credentials to the server. The default is + disable which means credentials will not be for= warded + to the server. Set this to enable to have + credentials forwarded when possible. + + + + service @@ -2479,6 +2491,25 @@ int PQconnectionUsedPassword(const PGconn *conn); + + + PQconnectionUsedGSSAPI= PQconnectionUsedGSSAPI + + + Returns true (1) if the connection authentication method + used GSSAPI. Returns false (0) if not. + + +int PQconnectionUsedGSSAPI(const PGconn *conn); + + + + + This function can be applied to detect whether the connection was + authenticated with GSSAPI. + + + =20 @@ -7981,6 +8012,16 @@ myEventProc(PGEventId evtId, void *evtInfo, void *pa= ssThrough) =20 + + + + PGGSSDELEG + + PGGSSDELEG behaves the same as the connection parameter. + + + diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml index b0b997f092..48cc5608b6 100644 --- a/doc/src/sgml/monitoring.sgml +++ b/doc/src/sgml/monitoring.sgml @@ -3573,6 +3573,15 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat= _activity WHERE wait_event i True if GSSAPI encryption is in use on this connection + + + + credentials_proxied boolean + + + True if GSSAPI credentials were forwarded/proxied on this connectio= n. + + diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/sys= tem_views.sql index 34ca0e739f..1843de1c77 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -977,7 +977,8 @@ CREATE VIEW pg_stat_gssapi AS S.pid, S.gss_auth AS gss_authenticated, S.gss_princ AS principal, - S.gss_enc AS encrypted + S.gss_enc AS encrypted, + S.gss_proxy AS credentials_proxied FROM pg_stat_get_activity(NULL) AS S WHERE S.client_port IS NOT NULL; =20 diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c index dca02271dc..6e1977fa62 100644 --- a/src/backend/foreign/foreign.c +++ b/src/backend/foreign/foreign.c @@ -574,6 +574,7 @@ static const struct ConnectionOption libpq_conninfo_opt= ions[] =3D { {"requiressl", ForeignServerRelationId}, {"sslmode", ForeignServerRelationId}, {"gsslib", ForeignServerRelationId}, + {"gssdeleg", ForeignServerRelationId}, {NULL, InvalidOid} }; =20 diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c index 25b3a781cd..963bc26b97 100644 --- a/src/backend/libpq/auth.c +++ b/src/backend/libpq/auth.c @@ -161,6 +161,7 @@ static int CheckCertAuth(Port *port); */ char *pg_krb_server_keyfile; bool pg_krb_caseins_users; +bool pg_gss_accept_deleg; =20 =20 /*---------------------------------------------------------------- @@ -914,6 +915,7 @@ pg_GSS_recvauth(Port *port) int mtype; StringInfoData buf; gss_buffer_desc gbuf; + gss_cred_id_t proxy; =20 /* * Use the configured keytab, if there is one. Unfortunately, Heimdal @@ -943,6 +945,9 @@ pg_GSS_recvauth(Port *port) */ port->gss->ctx =3D GSS_C_NO_CONTEXT; =20 + proxy =3D NULL; + port->gss->proxy_creds =3D false; + /* * Loop through GSSAPI message exchange. This exchange can consist of * multiple messages sent in both directions. First message is always from @@ -993,7 +998,7 @@ pg_GSS_recvauth(Port *port) &port->gss->outbuf, &gflags, NULL, - NULL); + pg_gss_accept_deleg ? &proxy : NULL); =20 /* gbuf no longer used */ pfree(buf.data); @@ -1005,6 +1010,12 @@ pg_GSS_recvauth(Port *port) =20 CHECK_FOR_INTERRUPTS(); =20 + if (proxy !=3D NULL) + { + pg_store_proxy_credential(proxy); + port->gss->proxy_creds =3D true; + } + if (port->gss->outbuf.length !=3D 0) { /* diff --git a/src/backend/libpq/be-gssapi-common.c b/src/backend/libpq/be-gs= sapi-common.c index fb39c760d8..d5a9926d5c 100644 --- a/src/backend/libpq/be-gssapi-common.c +++ b/src/backend/libpq/be-gssapi-common.c @@ -92,3 +92,54 @@ pg_GSS_error(const char *errmsg, (errmsg_internal("%s", errmsg), errdetail_internal("%s: %s", msg_major, msg_minor))); } + +/* + * Store the credentials passed in into the memory cache for later usage. + * + * This allows credentials to be delegated to us for us to use to connect + * to other systems with, using, e.g. postgres_fdw or dblink. + */ +#define GSS_MEMORY_CACHE "MEMORY:" +void +pg_store_proxy_credential(gss_cred_id_t cred) +{ + OM_uint32 major, minor; + gss_OID_set mech; + gss_cred_usage_t usage; + gss_key_value_element_desc cc; + gss_key_value_set_desc ccset; + + cc.key =3D "ccache"; + cc.value =3D GSS_MEMORY_CACHE; + ccset.count =3D 1; + ccset.elements =3D &cc; + + /* Make the proxy credential only available to current process */ + major =3D 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 !=3D GSS_S_COMPLETE) + { + pg_GSS_error("gss_store_cred", major, minor); + } + + /* Credential stored, so we can release our credential handle. */ + major =3D gss_release_cred(&minor, &cred); + if (major !=3D GSS_S_COMPLETE) + { + pg_GSS_error("gss_release_cred", major, minor); + } + + /* + * Set KRB5CCNAME for this backend, so that later calls to gss_acquire_cr= ed + * will find the proxied credentials we stored. + */ + setenv("KRB5CCNAME", GSS_MEMORY_CACHE, 1); +} diff --git a/src/backend/libpq/be-secure-gssapi.c b/src/backend/libpq/be-se= cure-gssapi.c index 3b55f43199..9ff91534e1 100644 --- a/src/backend/libpq/be-secure-gssapi.c +++ b/src/backend/libpq/be-secure-gssapi.c @@ -497,6 +497,7 @@ secure_open_gssapi(Port *port) bool complete_next =3D false; OM_uint32 major, minor; + gss_cred_id_t proxy; =20 /* * Allocate subsidiary Port data for GSSAPI operations. @@ -504,6 +505,9 @@ secure_open_gssapi(Port *port) port->gss =3D (pg_gssinfo *) MemoryContextAllocZero(TopMemoryContext, sizeof(pg_gssinfo)); =20 + proxy =3D NULL; + port->gss->proxy_creds =3D false; + /* * Allocate buffers and initialize state variables. By malloc'ing the * buffers at this point, we avoid wasting static data space in processes @@ -588,7 +592,8 @@ secure_open_gssapi(Port *port) GSS_C_NO_CREDENTIAL, &input, GSS_C_NO_CHANNEL_BINDINGS, &port->gss->name, NULL, &output, NULL, - NULL, NULL); + NULL, pg_gss_accept_deleg ? &proxy : NULL); + if (GSS_ERROR(major)) { pg_GSS_error(_("could not accept GSSAPI security context"), @@ -605,6 +610,12 @@ secure_open_gssapi(Port *port) complete_next =3D true; } =20 + if (proxy !=3D NULL) + { + pg_store_proxy_credential(proxy); + port->gss->proxy_creds =3D true; + } + /* Done handling the incoming packet, reset our buffer */ PqGSSRecvLength =3D 0; =20 @@ -731,3 +742,16 @@ be_gssapi_get_princ(Port *port) =20 return port->gss->princ; } + +/* + * Return if GSSAPI delegated/proxy credentials were included on this + * connection. + */ +bool +be_gssapi_get_proxy(Port *port) +{ + if (!port || !port->gss) + return NULL; + + return port->gss->proxy_creds; +} diff --git a/src/backend/utils/activity/backend_status.c b/src/backend/util= s/activity/backend_status.c index 608d01ea0d..778d5769fd 100644 --- a/src/backend/utils/activity/backend_status.c +++ b/src/backend/utils/activity/backend_status.c @@ -384,6 +384,7 @@ pgstat_bestart(void) lbeentry.st_gss =3D true; lgssstatus.gss_auth =3D be_gssapi_get_auth(MyProcPort); lgssstatus.gss_enc =3D be_gssapi_get_enc(MyProcPort); + lgssstatus.gss_proxy =3D be_gssapi_get_proxy(MyProcPort); if (princ) strlcpy(lgssstatus.gss_princ, princ, NAMEDATALEN); } diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pg= statfuncs.c index 9d707c3521..cf774a6848 100644 --- a/src/backend/utils/adt/pgstatfuncs.c +++ b/src/backend/utils/adt/pgstatfuncs.c @@ -303,7 +303,7 @@ pg_stat_get_progress_info(PG_FUNCTION_ARGS) Datum pg_stat_get_activity(PG_FUNCTION_ARGS) { -#define PG_STAT_GET_ACTIVITY_COLS 30 +#define PG_STAT_GET_ACTIVITY_COLS 31 int num_backends =3D pgstat_fetch_stat_numbackends(); int curr_backend; int pid =3D PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0); @@ -395,7 +395,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS) pfree(clipped_activity); =20 /* leader_pid */ - nulls[28] =3D true; + nulls[29] =3D true; =20 proc =3D BackendPidGetProc(beentry->st_procpid); =20 @@ -432,8 +432,8 @@ pg_stat_get_activity(PG_FUNCTION_ARGS) */ if (leader && leader->pid !=3D beentry->st_procpid) { - values[28] =3D Int32GetDatum(leader->pid); - nulls[28] =3D false; + values[29] =3D Int32GetDatum(leader->pid); + nulls[29] =3D false; } else if (beentry->st_backendType =3D=3D B_BG_WORKER) { @@ -441,8 +441,8 @@ pg_stat_get_activity(PG_FUNCTION_ARGS) =20 if (leader_pid !=3D InvalidPid) { - values[28] =3D Int32GetDatum(leader_pid); - nulls[28] =3D false; + values[29] =3D Int32GetDatum(leader_pid); + nulls[29] =3D false; } } } @@ -600,6 +600,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS) values[25] =3D BoolGetDatum(beentry->st_gssstatus->gss_auth); /* gss_a= uth */ values[26] =3D CStringGetTextDatum(beentry->st_gssstatus->gss_princ); values[27] =3D BoolGetDatum(beentry->st_gssstatus->gss_enc); /* GSS En= cryption in use */ + values[28] =3D BoolGetDatum(beentry->st_gssstatus->gss_proxy); /* GSS = credentials proxied */ } else { @@ -607,11 +608,13 @@ pg_stat_get_activity(PG_FUNCTION_ARGS) nulls[26] =3D true; /* No GSS principal */ values[27] =3D BoolGetDatum(false); /* GSS Encryption not in * use */ + values[28] =3D BoolGetDatum(false); /* GSS credentials not + * proxied */ } if (beentry->st_query_id =3D=3D 0) - nulls[29] =3D true; + nulls[30] =3D true; else - values[29] =3D UInt64GetDatum(beentry->st_query_id); + values[30] =3D UInt64GetDatum(beentry->st_query_id); } else { @@ -640,6 +643,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS) nulls[27] =3D true; nulls[28] =3D true; nulls[29] =3D true; + nulls[30] =3D true; } =20 tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls); diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/pos= tinit.c index 2f07ca7a0e..c872512bbc 100644 --- a/src/backend/utils/init/postinit.c +++ b/src/backend/utils/init/postinit.c @@ -282,15 +282,17 @@ PerformAuthentication(Port *port) =20 if (princ) appendStringInfo(&logmsg, - _(" GSS (authenticated=3D%s, encrypted=3D%s, principal=3D%s)"), + _(" GSS (authenticated=3D%s, encrypted=3D%s, proxy_credentials=3D= %s, principal=3D%s)"), be_gssapi_get_auth(port) ? _("yes") : _("no"), be_gssapi_get_enc(port) ? _("yes") : _("no"), + be_gssapi_get_proxy(port) ? _("yes") : _("no"), princ); else appendStringInfo(&logmsg, - _(" GSS (authenticated=3D%s, encrypted=3D%s)"), + _(" GSS (authenticated=3D%s, encrypted=3D%s, proxy_credentials=3D= %s)"), be_gssapi_get_auth(port) ? _("yes") : _("no"), - be_gssapi_get_enc(port) ? _("yes") : _("no")); + be_gssapi_get_enc(port) ? _("yes") : _("no"), + be_gssapi_get_proxy(port) ? _("yes") : _("no")); } #endif =20 diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/g= uc_tables.c index 1c0583fe26..6d7d5908ef 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -1708,6 +1708,16 @@ struct config_bool ConfigureNamesBool[] =3D NULL, NULL, NULL }, =20 + { + {"gss_accept_deleg", PGC_SIGHUP, CONN_AUTH_AUTH, + gettext_noop("Sets whether GSSAPI delegation should be accepted from th= e client."), + NULL + }, + &pg_gss_accept_deleg, + false, + NULL, NULL, NULL + }, + { {"escape_string_warning", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS, gettext_noop("Warn about backslash escapes in ordinary string literals.= "), diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/ut= ils/misc/postgresql.conf.sample index d06074b86f..7d26d58de3 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -100,6 +100,7 @@ # GSSAPI using Kerberos #krb_server_keyfile =3D 'FILE:${sysconfdir}/krb5.keytab' #krb_caseins_users =3D off +#gss_accept_deleg =3D off =20 # - SSL - =20 diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.= dat index 66b73c3900..44f05cfac0 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -5404,9 +5404,9 @@ proname =3D> 'pg_stat_get_activity', prorows =3D> '100', proisstrict =3D= > 'f', proretset =3D> 't', provolatile =3D> 's', proparallel =3D> 'r', prorettype =3D> 'record', proargtypes =3D> 'int4', - proallargtypes =3D> '{int4,oid,int4,oid,text,text,text,text,text,timesta= mptz,timestamptz,timestamptz,timestamptz,inet,text,int4,xid,xid,text,bool,t= ext,text,int4,text,numeric,text,bool,text,bool,int4,int8}', - proargmodes =3D> '{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 =3D> '{pid,datid,pid,usesysid,application_name,state,query,w= ait_event_type,wait_event,xact_start,query_start,backend_start,state_change= ,client_addr,client_hostname,client_port,backend_xid,backend_xmin,backend_t= ype,ssl,sslversion,sslcipher,sslbits,ssl_client_dn,ssl_client_serial,ssl_is= suer_dn,gss_auth,gss_princ,gss_enc,leader_pid,query_id}', + proallargtypes =3D> '{int4,oid,int4,oid,text,text,text,text,text,timesta= mptz,timestamptz,timestamptz,timestamptz,inet,text,int4,xid,xid,text,bool,t= ext,text,int4,text,numeric,text,bool,text,bool,bool,int4,int8}', + proargmodes =3D> '{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 =3D> '{pid,datid,pid,usesysid,application_name,state,query,w= ait_event_type,wait_event,xact_start,query_start,backend_start,state_change= ,client_addr,client_hostname,client_port,backend_xid,backend_xmin,backend_t= ype,ssl,sslversion,sslcipher,sslbits,ssl_client_dn,ssl_client_serial,ssl_is= suer_dn,gss_auth,gss_princ,gss_enc,gss_proxy,leader_pid,query_id}', prosrc =3D> 'pg_stat_get_activity' }, { oid =3D> '3318', descr =3D> 'statistics: information about progress of backends running m= aintenance command', diff --git a/src/include/libpq/auth.h b/src/include/libpq/auth.h index 137bee7c45..9105d6757d 100644 --- a/src/include/libpq/auth.h +++ b/src/include/libpq/auth.h @@ -18,6 +18,7 @@ =20 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; =20 extern void ClientAuthentication(Port *port); diff --git a/src/include/libpq/be-gssapi-common.h b/src/include/libpq/be-gs= sapi-common.h index facd24ff7f..841facde12 100644 --- a/src/include/libpq/be-gssapi-common.h +++ b/src/include/libpq/be-gssapi-common.h @@ -18,13 +18,16 @@ =20 #if defined(HAVE_GSSAPI_H) #include +#include #else #include +#include #endif =20 extern void pg_GSS_error(const char *errmsg, OM_uint32 maj_stat, OM_uint32 min_stat); =20 +extern void pg_store_proxy_credential(gss_cred_id_t cred); #endif /* ENABLE_GSS */ =20 #endif /* BE_GSSAPI_COMMON_H */ diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h index ac6407e9f6..4d6a8c31cb 100644 --- a/src/include/libpq/libpq-be.h +++ b/src/include/libpq/libpq-be.h @@ -84,6 +84,7 @@ typedef struct * GSSAPI auth was not used */ bool auth; /* GSSAPI Authentication used */ bool enc; /* GSSAPI encryption in use */ + bool proxy_creds; /* GSSAPI Delegated/proxy credentials */ #endif } pg_gssinfo; #endif @@ -328,6 +329,7 @@ extern PGDLLIMPORT openssl_tls_init_hook_typ openssl_tl= s_init_hook; extern bool be_gssapi_get_auth(Port *port); extern bool be_gssapi_get_enc(Port *port); extern const char *be_gssapi_get_princ(Port *port); +extern bool be_gssapi_get_proxy(Port *port); =20 /* Read and write to a GSSAPI-encrypted connection. */ extern ssize_t be_gssapi_read(Port *port, void *ptr, size_t len); diff --git a/src/include/utils/backend_status.h b/src/include/utils/backend= _status.h index f7bd83113a..0e680f0332 100644 --- a/src/include/utils/backend_status.h +++ b/src/include/utils/backend_status.h @@ -77,6 +77,7 @@ typedef struct PgBackendGSSStatus char gss_princ[NAMEDATALEN]; /* GSSAPI Principal used to auth */ bool gss_auth; /* If GSSAPI authentication was used */ bool gss_enc; /* If encryption is being used */ + bool gss_proxy; /* If credentials proxied */ =20 } PgBackendGSSStatus; =20 diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/export= s.txt index e8bcc88370..7ded77aff3 100644 --- a/src/interfaces/libpq/exports.txt +++ b/src/interfaces/libpq/exports.txt @@ -186,3 +186,4 @@ PQpipelineStatus 183 PQsetTraceFlags 184 PQmblenBounded 185 PQsendFlushRequest 186 +PQconnectionUsedGSSAPI 187 diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c index 9afc6f19b9..a518378915 100644 --- a/src/interfaces/libpq/fe-auth.c +++ b/src/interfaces/libpq/fe-auth.c @@ -58,9 +58,11 @@ pg_GSS_continue(PGconn *conn, int payloadlen) { OM_uint32 maj_stat, min_stat, - lmin_s; + lmin_s, + gss_flags =3D GSS_C_MUTUAL_FLAG; gss_buffer_desc ginbuf; gss_buffer_desc goutbuf; + gss_cred_id_t proxy =3D GSS_C_NO_CREDENTIAL; =20 /* * On first call, there's no input token. On subsequent calls, read the @@ -92,12 +94,19 @@ pg_GSS_continue(PGconn *conn, int payloadlen) ginbuf.value =3D NULL; } =20 + /* Only try to acquire credentials if GSS delegation isn't disabled. */ + if (!pg_GSS_have_cred_cache(&proxy)) + proxy =3D GSS_C_NO_CREDENTIAL; + + if (conn->gssdeleg && pg_strcasecmp(conn->gssdeleg,"enable") =3D=3D 0) + gss_flags |=3D GSS_C_DELEG_FLAG; + maj_stat =3D gss_init_sec_context(&min_stat, - GSS_C_NO_CREDENTIAL, + proxy, &conn->gctx, conn->gtarg_nam, GSS_C_NO_OID, - GSS_C_MUTUAL_FLAG, + gss_flags, 0, GSS_C_NO_CHANNEL_BINDINGS, (ginbuf.value =3D=3D NULL) ? GSS_C_NO_BUFFER : &ginbuf, @@ -136,7 +145,10 @@ pg_GSS_continue(PGconn *conn, int payloadlen) } =20 if (maj_stat =3D=3D GSS_S_COMPLETE) + { gss_release_name(&lmin_s, &conn->gtarg_nam); + conn->gssapi_used =3D true; + } =20 return STATUS_OK; } diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-co= nnect.c index 50b5df3490..69c7e1346a 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -332,6 +332,10 @@ static const internalPQconninfoOption PQconninfoOption= s[] =3D { "GSS-library", "", 7, /* sizeof("gssapi") =3D=3D 7 */ offsetof(struct pg_conn, gsslib)}, =20 + {"gssdeleg", "PGGSSDELEG", NULL, NULL, + "GSS-delegation", "", 8, /* sizeof("disable") =3D=3D 8 */ + offsetof(struct pg_conn, gssdeleg)}, + {"replication", NULL, NULL, NULL, "Replication", "D", 5, offsetof(struct pg_conn, replication)}, @@ -596,6 +600,7 @@ pqDropServerData(PGconn *conn) conn->last_sqlstate[0] =3D '\0'; conn->auth_req_received =3D false; conn->password_needed =3D false; + conn->gssapi_used =3D false; conn->write_failed =3D false; free(conn->write_err_msg); conn->write_err_msg =3D NULL; @@ -4028,6 +4033,7 @@ freePGconn(PGconn *conn) free(conn->gssencmode); free(conn->krbsrvname); free(conn->gsslib); + free(conn->gssdeleg); free(conn->connip); /* Note that conn->Pfdebug is not ours to close or free */ free(conn->write_err_msg); @@ -6816,6 +6822,17 @@ PQconnectionUsedPassword(const PGconn *conn) return false; } =20 +int +PQconnectionUsedGSSAPI(const PGconn *conn) +{ + if (!conn) + return false; + if (conn->gssapi_used) + return true; + else + return false; +} + int PQclientEncoding(const PGconn *conn) { diff --git a/src/interfaces/libpq/fe-secure-gssapi.c b/src/interfaces/libpq= /fe-secure-gssapi.c index 038e847b7e..372bfdd21e 100644 --- a/src/interfaces/libpq/fe-secure-gssapi.c +++ b/src/interfaces/libpq/fe-secure-gssapi.c @@ -477,11 +477,13 @@ pqsecure_open_gss(PGconn *conn) { ssize_t ret; OM_uint32 major, - minor; + minor, + gss_flags =3D GSS_REQUIRED_FLAGS; uint32 netlen; PostgresPollingStatusType result; gss_buffer_desc input =3D GSS_C_EMPTY_BUFFER, output =3D GSS_C_EMPTY_BUFFER; + static gss_cred_id_t proxy =3D GSS_C_NO_CREDENTIAL; =20 /* * If first time through for this connection, allocate buffers and @@ -621,13 +623,30 @@ pqsecure_open_gss(PGconn *conn) if (ret !=3D STATUS_OK) return PGRES_POLLING_FAILED; =20 + if (conn->gssdeleg && pg_strcasecmp(conn->gssdeleg,"enable") =3D=3D 0) + { + /* Acquire credentials if possbile */ + if (proxy =3D=3D GSS_C_NO_CREDENTIAL) + (void) pg_GSS_have_cred_cache(&proxy); + + /* + * We have credentials and gssdeleg is enabled, so request + * credential delegation. This may or may not actually result in + * credentials being delegated- it depends on if the forwardable + * flag has been set in the credential and if the server is + * configured to accept delegated credentials. + */ + if (proxy !=3D GSS_C_NO_CREDENTIAL) + gss_flags |=3D GSS_C_DELEG_FLAG; + } + /* * Call GSS init context, either with an empty input, or with a complete * packet from the server. */ - major =3D gss_init_sec_context(&minor, conn->gcred, &conn->gctx, + major =3D gss_init_sec_context(&minor, proxy, &conn->gctx, conn->gtarg_nam, GSS_C_NO_OID, - GSS_REQUIRED_FLAGS, 0, 0, &input, NULL, + gss_flags, 0, 0, &input, NULL, &output, NULL, NULL); =20 /* GSS Init Sec Context uses the whole packet, so clear it */ @@ -647,6 +666,7 @@ pqsecure_open_gss(PGconn *conn) * to do GSS wrapping/unwrapping. */ conn->gssenc =3D true; + conn->gssapi_used =3D true; =20 /* Clean up */ gss_release_cred(&minor, &conn->gcred); diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-f= e.h index f3d9220496..7476dbe0e9 100644 --- a/src/interfaces/libpq/libpq-fe.h +++ b/src/interfaces/libpq/libpq-fe.h @@ -354,6 +354,7 @@ extern int PQbackendPID(const PGconn *conn); extern PGpipelineStatus PQpipelineStatus(const PGconn *conn); extern int PQconnectionNeedsPassword(const PGconn *conn); extern int PQconnectionUsedPassword(const PGconn *conn); +extern int PQconnectionUsedGSSAPI(const PGconn *conn); extern int PQclientEncoding(const PGconn *conn); extern int PQsetClientEncoding(PGconn *conn, const char *encoding); =20 diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-= int.h index d7ec5ed429..4828917d38 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -393,6 +393,7 @@ struct pg_conn char *krbsrvname; /* Kerberos service name */ char *gsslib; /* What GSS library to use ("gssapi" or * "sspi") */ + char *gssdeleg; /* Try to delegate GSS credentials? */ char *ssl_min_protocol_version; /* minimum TLS protocol version */ char *ssl_max_protocol_version; /* maximum TLS protocol version */ char *target_session_attrs; /* desired session properties */ @@ -452,6 +453,7 @@ struct pg_conn int sversion; /* server version, e.g. 70401 for 7.4.1 */ bool auth_req_received; /* true if any type of auth req received */ bool password_needed; /* true if server demanded a password */ + bool gssapi_used; /* true if authenticated via gssapi */ bool sigpipe_so; /* have we masked SIGPIPE via SO_NOSIGPIPE? */ bool sigpipe_flag; /* can we mask SIGPIPE via MSG_NOSIGNAL? */ bool write_failed; /* have we had a write failure on sock? */ diff --git a/src/test/kerberos/Makefile b/src/test/kerberos/Makefile index 7765f3f93b..f460d2c0e7 100644 --- a/src/test/kerberos/Makefile +++ b/src/test/kerberos/Makefile @@ -13,6 +13,9 @@ subdir =3D src/test/kerberos top_builddir =3D ../../.. include $(top_builddir)/src/Makefile.global =20 +EXTRA_INSTALL +=3D contrib/postgres_fdw +EXTRA_INSTALL +=3D contrib/dblink + export with_gssapi with_krb_srvnam =20 check: diff --git a/src/test/kerberos/t/001_auth.pl b/src/test/kerberos/t/001_auth= =2Epl index d610ce63ab..6e76ef6373 100644 --- a/src/test/kerberos/t/001_auth.pl +++ b/src/test/kerberos/t/001_auth.pl @@ -56,6 +56,7 @@ elsif ($^O eq 'linux') =20 my $krb5_config =3D 'krb5-config'; my $kinit =3D 'kinit'; +my $klist =3D 'klist'; my $kdb5_util =3D 'kdb5_util'; my $kadmin_local =3D 'kadmin.local'; my $krb5kdc =3D 'krb5kdc'; @@ -64,6 +65,7 @@ if ($krb5_bin_dir && -d $krb5_bin_dir) { $krb5_config =3D $krb5_bin_dir . '/' . $krb5_config; $kinit =3D $krb5_bin_dir . '/' . $kinit; + $klist =3D $krb5_bin_dir . '/' . $klist; } if ($krb5_sbin_dir && -d $krb5_sbin_dir) { @@ -108,6 +110,7 @@ kdc =3D FILE:$kdc_log =20 [libdefaults] default_realm =3D $realm +forwardable =3D false =20 [realms] $realm =3D { @@ -185,7 +188,21 @@ lc_messages =3D 'C' }); $node->start; =20 +my $port =3D $node->port(); + $node->safe_psql('postgres', 'CREATE USER test1;'); +$node->safe_psql('postgres', 'CREATE EXTENSION postgres_fdw;'); +$node->safe_psql('postgres', 'CREATE EXTENSION dblink;'); +$node->safe_psql('postgres', "CREATE SERVER s1 FOREIGN DATA WRAPPER postgr= es_fdw OPTIONS (host '$host', hostaddr '$hostaddr', port '$port', dbname 'p= ostgres');"); + +$node->safe_psql('postgres', 'GRANT USAGE ON FOREIGN SERVER s1 TO test1;'); + +$node->safe_psql('postgres', "CREATE USER MAPPING FOR test1 SERVER s1 OPTI= ONS (user 'test1', password_required 'false');"); +$node->safe_psql('postgres', "CREATE TABLE t1 (c1 int);"); +$node->safe_psql('postgres', "INSERT INTO t1 VALUES (1);"); +$node->safe_psql('postgres', "CREATE FOREIGN TABLE tf1 (c1 int) SERVER s1 = OPTIONS (schema_name 'public', table_name 't1');"); +$node->safe_psql('postgres', "GRANT SELECT ON t1 TO test1;"); +$node->safe_psql('postgres', "GRANT SELECT ON tf1 TO test1;"); =20 # Set up a table for SYSTEM_USER parallel worker testing. $node->safe_psql('postgres', @@ -258,6 +275,7 @@ $node->restart; test_access($node, 'test1', 'SELECT true', 2, '', 'fails without ticket'); =20 run_log [ $kinit, 'test1' ], \$test1_password or BAIL_OUT($?); +run_log [ $klist, '-f' ] or BAIL_OUT($?); =20 test_access( $node, @@ -275,34 +293,56 @@ $node->restart; test_access( $node, 'test1', - 'SELECT gss_authenticated AND encrypted from pg_stat_gssapi where pid =3D= pg_backend_pid();', + 'SELECT gss_authenticated AND encrypted AND NOT credentials_proxied FROM = pg_stat_gssapi WHERE pid =3D pg_backend_pid();', 0, '', - 'succeeds with mapping with default gssencmode and host hba', + 'succeeds with mapping with default gssencmode and host hba, ticket not f= orwardable', "connection authenticated: identity=3D\"test1\@$realm\" method=3Dgss", - "connection authorized: user=3D$username database=3D$dbname application_n= ame=3D$application GSS (authenticated=3Dyes, encrypted=3Dyes, principal=3Dt= est1\@$realm)" + "connection authorized: user=3D$username database=3D$dbname application_n= ame=3D$application GSS (authenticated=3Dyes, encrypted=3Dyes, proxy_credent= ials=3Dno, principal=3Dtest1\@$realm)" ); =20 test_access( $node, 'test1', - 'SELECT gss_authenticated AND encrypted from pg_stat_gssapi where pid =3D= pg_backend_pid();', + 'SELECT gss_authenticated AND encrypted AND NOT credentials_proxied FROM = pg_stat_gssapi WHERE pid =3D pg_backend_pid();', 0, 'gssencmode=3Dprefer', - 'succeeds with GSS-encrypted access preferred with host hba', + 'succeeds with GSS-encrypted access preferred with host hba, ticket not f= orwardable', "connection authenticated: identity=3D\"test1\@$realm\" method=3Dgss", - "connection authorized: user=3D$username database=3D$dbname application_n= ame=3D$application GSS (authenticated=3Dyes, encrypted=3Dyes, principal=3Dt= est1\@$realm)" + "connection authorized: user=3D$username database=3D$dbname application_n= ame=3D$application GSS (authenticated=3Dyes, encrypted=3Dyes, proxy_credent= ials=3Dno, principal=3Dtest1\@$realm)" ); test_access( $node, 'test1', - 'SELECT gss_authenticated AND encrypted from pg_stat_gssapi where pid =3D= pg_backend_pid();', + 'SELECT gss_authenticated AND encrypted AND NOT credentials_proxied FROM = pg_stat_gssapi WHERE pid =3D pg_backend_pid();', 0, 'gssencmode=3Drequire', - 'succeeds with GSS-encrypted access required with host hba', + 'succeeds with GSS-encrypted access required with host hba, ticket not fo= rwardable', + "connection authenticated: identity=3D\"test1\@$realm\" method=3Dgss", + "connection authorized: user=3D$username database=3D$dbname application_n= ame=3D$application GSS (authenticated=3Dyes, encrypted=3Dyes, proxy_credent= ials=3Dno, principal=3Dtest1\@$realm)" +); + +test_access( + $node, + 'test1', + 'SELECT gss_authenticated AND encrypted AND NOT credentials_proxied FROM = pg_stat_gssapi WHERE pid =3D pg_backend_pid();', + 0, + 'gssencmode=3Dprefer gssdeleg=3Denable', + 'succeeds with GSS-encrypted access preferred with host hba and credentia= ls not delegated even though asked for (ticket not forwardable)', "connection authenticated: identity=3D\"test1\@$realm\" method=3Dgss", - "connection authorized: user=3D$username database=3D$dbname application_n= ame=3D$application GSS (authenticated=3Dyes, encrypted=3Dyes, principal=3Dt= est1\@$realm)" + "connection authorized: user=3D$username database=3D$dbname application_n= ame=3D$application GSS (authenticated=3Dyes, encrypted=3Dyes, proxy_credent= ials=3Dno, principal=3Dtest1\@$realm)" ); +test_access( + $node, + 'test1', + 'SELECT gss_authenticated AND encrypted AND NOT credentials_proxied FROM = pg_stat_gssapi WHERE pid =3D pg_backend_pid();', + 0, + 'gssencmode=3Drequire gssdeleg=3Denable', + 'succeeds with GSS-encrypted access required with host hba and credential= s not delegated even though asked for (ticket not forwardable)', + "connection authenticated: identity=3D\"test1\@$realm\" method=3Dgss", + "connection authorized: user=3D$username database=3D$dbname application_n= ame=3D$application GSS (authenticated=3Dyes, encrypted=3Dyes, proxy_credent= ials=3Dno, principal=3Dtest1\@$realm)" +); + =20 # Test that we can transport a reasonable amount of data. test_query( @@ -345,27 +385,101 @@ test_query( unlink($node->data_dir . '/pg_hba.conf'); $node->append_conf('pg_hba.conf', qq{hostgssenc all all $hostaddr/32 gss map=3Dmymap}); + +string_replace_file($krb5_conf, "forwardable =3D false", "forwardable =3D = true"); + +run_log [ $kinit, 'test1' ], \$test1_password or BAIL_OUT($?); +run_log [ $klist, '-f' ] or BAIL_OUT($?); + +test_access( + $node, + 'test1', + 'SELECT gss_authenticated AND encrypted AND NOT credentials_proxied from = pg_stat_gssapi where pid =3D pg_backend_pid();', + 0, + 'gssencmode=3Dprefer gssdeleg=3Denable', + 'succeeds with GSS-encrypted access preferred and hostgssenc hba and cred= entials not forwarded (server does not accept them, default)', + "connection authenticated: identity=3D\"test1\@$realm\" method=3Dgss", + "connection authorized: user=3D$username database=3D$dbname application_n= ame=3D$application GSS (authenticated=3Dyes, encrypted=3Dyes, proxy_credent= ials=3Dno, principal=3Dtest1\@$realm)" +); +test_access( + $node, + 'test1', + 'SELECT gss_authenticated AND encrypted AND NOT credentials_proxied from = pg_stat_gssapi where pid =3D pg_backend_pid();', + 0, + 'gssencmode=3Drequire gssdeleg=3Denable', + 'succeeds with GSS-encrypted access required and hostgssenc hba and crede= ntials not forwarded (server does not accept them, default)', + "connection authenticated: identity=3D\"test1\@$realm\" method=3Dgss", + "connection authorized: user=3D$username database=3D$dbname application_n= ame=3D$application GSS (authenticated=3Dyes, encrypted=3Dyes, proxy_credent= ials=3Dno, principal=3Dtest1\@$realm)" +); + +$node->append_conf('postgresql.conf', + qq{gss_accept_deleg=3Doff}); +$node->restart; + +test_access( + $node, + 'test1', + 'SELECT gss_authenticated AND encrypted AND NOT credentials_proxied from = pg_stat_gssapi where pid =3D pg_backend_pid();', + 0, + 'gssencmode=3Dprefer gssdeleg=3Denable', + 'succeeds with GSS-encrypted access preferred and hostgssenc hba and cred= entials not forwarded (server does not accept them, explicitly disabled)', + "connection authenticated: identity=3D\"test1\@$realm\" method=3Dgss", + "connection authorized: user=3D$username database=3D$dbname application_n= ame=3D$application GSS (authenticated=3Dyes, encrypted=3Dyes, proxy_credent= ials=3Dno, principal=3Dtest1\@$realm)" +); +test_access( + $node, + 'test1', + 'SELECT gss_authenticated AND encrypted AND NOT credentials_proxied from = pg_stat_gssapi where pid =3D pg_backend_pid();', + 0, + 'gssencmode=3Drequire gssdeleg=3Denable', + 'succeeds with GSS-encrypted access required and hostgssenc hba and crede= ntials not forwarded (server does not accept them, explicitly disabled)', + "connection authenticated: identity=3D\"test1\@$realm\" method=3Dgss", + "connection authorized: user=3D$username database=3D$dbname application_n= ame=3D$application GSS (authenticated=3Dyes, encrypted=3Dyes, proxy_credent= ials=3Dno, principal=3Dtest1\@$realm)" +); + +$node->append_conf('postgresql.conf', + qq{gss_accept_deleg=3Don}); $node->restart; =20 test_access( $node, 'test1', - 'SELECT gss_authenticated AND encrypted from pg_stat_gssapi where pid =3D= pg_backend_pid();', + 'SELECT gss_authenticated AND encrypted AND credentials_proxied from pg_s= tat_gssapi where pid =3D pg_backend_pid();', + 0, + 'gssencmode=3Dprefer gssdeleg=3Denable', + 'succeeds with GSS-encrypted access preferred and hostgssenc hba and cred= entials forwarded', + "connection authenticated: identity=3D\"test1\@$realm\" method=3Dgss", + "connection authorized: user=3D$username database=3D$dbname application_n= ame=3D$application GSS (authenticated=3Dyes, encrypted=3Dyes, proxy_credent= ials=3Dyes, principal=3Dtest1\@$realm)" +); +test_access( + $node, + 'test1', + 'SELECT gss_authenticated AND encrypted AND credentials_proxied from pg_s= tat_gssapi where pid =3D pg_backend_pid();', + 0, + 'gssencmode=3Drequire gssdeleg=3Denable', + 'succeeds with GSS-encrypted access required and hostgssenc hba and crede= ntials forwarded', + "connection authenticated: identity=3D\"test1\@$realm\" method=3Dgss", + "connection authorized: user=3D$username database=3D$dbname application_n= ame=3D$application GSS (authenticated=3Dyes, encrypted=3Dyes, proxy_credent= ials=3Dyes, principal=3Dtest1\@$realm)" +); +test_access( + $node, + 'test1', + 'SELECT gss_authenticated AND encrypted AND NOT credentials_proxied FROM = pg_stat_gssapi WHERE pid =3D pg_backend_pid();', 0, 'gssencmode=3Dprefer', - 'succeeds with GSS-encrypted access preferred and hostgssenc hba', + 'succeeds with GSS-encrypted access preferred and hostgssenc hba and cred= entials not forwarded', "connection authenticated: identity=3D\"test1\@$realm\" method=3Dgss", - "connection authorized: user=3D$username database=3D$dbname application_n= ame=3D$application GSS (authenticated=3Dyes, encrypted=3Dyes, principal=3Dt= est1\@$realm)" + "connection authorized: user=3D$username database=3D$dbname application_n= ame=3D$application GSS (authenticated=3Dyes, encrypted=3Dyes, proxy_credent= ials=3Dno, principal=3Dtest1\@$realm)" ); test_access( $node, 'test1', - 'SELECT gss_authenticated AND encrypted from pg_stat_gssapi where pid =3D= pg_backend_pid();', + 'SELECT gss_authenticated AND encrypted AND NOT credentials_proxied FROM = pg_stat_gssapi WHERE pid =3D pg_backend_pid();', 0, - 'gssencmode=3Drequire', - 'succeeds with GSS-encrypted access required and hostgssenc hba', + 'gssencmode=3Drequire gssdeleg=3Ddisable', + 'succeeds with GSS-encrypted access required and hostgssenc hba and crede= ntials not forwarded', "connection authenticated: identity=3D\"test1\@$realm\" method=3Dgss", - "connection authorized: user=3D$username database=3D$dbname application_n= ame=3D$application GSS (authenticated=3Dyes, encrypted=3Dyes, principal=3Dt= est1\@$realm)" + "connection authorized: user=3D$username database=3D$dbname application_n= ame=3D$application GSS (authenticated=3Dyes, encrypted=3Dyes, proxy_credent= ials=3Dno, principal=3Dtest1\@$realm)" ); test_access($node, 'test1', 'SELECT true', 2, 'gssencmode=3Ddisable', 'fails with GSS encryption disabled and hostgssenc hba'); @@ -378,26 +492,42 @@ $node->restart; test_access( $node, 'test1', - 'SELECT gss_authenticated and not encrypted from pg_stat_gssapi where pid= =3D pg_backend_pid();', + 'SELECT gss_authenticated AND NOT encrypted AND credentials_proxied FROM = pg_stat_gssapi WHERE pid =3D pg_backend_pid();', 0, - 'gssencmode=3Dprefer', + 'gssencmode=3Dprefer gssdeleg=3Denable', 'succeeds with GSS-encrypted access preferred and hostnogssenc hba, but n= o encryption', "connection authenticated: identity=3D\"test1\@$realm\" method=3Dgss", - "connection authorized: user=3D$username database=3D$dbname application_n= ame=3D$application GSS (authenticated=3Dyes, encrypted=3Dno, principal=3Dte= st1\@$realm)" + "connection authorized: user=3D$username database=3D$dbname application_n= ame=3D$application GSS (authenticated=3Dyes, encrypted=3Dno, proxy_credenti= als=3Dyes, principal=3Dtest1\@$realm)" ); test_access($node, 'test1', 'SELECT true', 2, 'gssencmode=3Drequire', '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= =3D pg_backend_pid();', + 'SELECT gss_authenticated AND NOT encrypted AND credentials_proxied FROM = pg_stat_gssapi WHERE pid =3D pg_backend_pid();', 0, - 'gssencmode=3Ddisable', + 'gssencmode=3Ddisable gssdeleg=3Denable', 'succeeds with GSS encryption disabled and hostnogssenc hba', "connection authenticated: identity=3D\"test1\@$realm\" method=3Dgss", - "connection authorized: user=3D$username database=3D$dbname application_n= ame=3D$application GSS (authenticated=3Dyes, encrypted=3Dno, principal=3Dte= st1\@$realm)" + "connection authorized: user=3D$username database=3D$dbname application_n= ame=3D$application GSS (authenticated=3Dyes, encrypted=3Dno, proxy_credenti= als=3Dyes, principal=3Dtest1\@$realm)" ); =20 +test_query( + $node, + 'test1', + "SELECT * FROM dblink('user=3Dtest1 dbname=3D$dbname host=3D$host hostadd= r=3D$hostaddr port=3D$port password=3D1234','select 1') as t1(c1 int);", + qr/^1$/s, + 'gssencmode=3Dprefer gssdeleg=3Denable', + 'dblink works not-encrypted'); + +test_query( + $node, + 'test1', + "TABLE tf1;", + qr/^1$/s, + 'gssencmode=3Dprefer gssdeleg=3Denable', + 'postgres_fdw works not-encrypted'); + truncate($node->data_dir . '/pg_ident.conf', 0); unlink($node->data_dir . '/pg_hba.conf'); $node->append_conf('pg_hba.conf', @@ -407,14 +537,30 @@ $node->restart; test_access( $node, 'test1', - 'SELECT gss_authenticated AND encrypted from pg_stat_gssapi where pid =3D= pg_backend_pid();', + 'SELECT gss_authenticated AND encrypted AND credentials_proxied FROM pg_s= tat_gssapi WHERE pid =3D pg_backend_pid();', 0, - '', + 'gssdeleg=3Denable', 'succeeds with include_realm=3D0 and defaults', "connection authenticated: identity=3D\"test1\@$realm\" method=3Dgss", - "connection authorized: user=3D$username database=3D$dbname application_n= ame=3D$application GSS (authenticated=3Dyes, encrypted=3Dyes, principal=3Dt= est1\@$realm)" + "connection authorized: user=3D$username database=3D$dbname application_n= ame=3D$application GSS (authenticated=3Dyes, encrypted=3Dyes, proxy_credent= ials=3Dyes, principal=3Dtest1\@$realm)" ); =20 +test_query( + $node, + 'test1', + "SELECT * FROM dblink('user=3Dtest1 dbname=3D$dbname host=3D$host hostadd= r=3D$hostaddr port=3D$port password=3D1234','select 1') as t1(c1 int);", + qr/^1$/s, + 'gssencmode=3Drequire gssdeleg=3Denable', + 'dblink works encrypted'); + +test_query( + $node, + 'test1', + "TABLE tf1;", + qr/^1$/s, + 'gssencmode=3Drequire gssdeleg=3Denable', + 'postgres_fdw works encrypted'); + # Reset pg_hba.conf, and cause a usermap failure with an authentication # that has passed. unlink($node->data_dir . '/pg_hba.conf'); diff --git a/src/test/perl/PostgreSQL/Test/Utils.pm b/src/test/perl/Postgre= SQL/Test/Utils.pm index 878e12b15e..9249954b49 100644 --- a/src/test/perl/PostgreSQL/Test/Utils.pm +++ b/src/test/perl/PostgreSQL/Test/Utils.pm @@ -65,6 +65,7 @@ our @EXPORT =3D 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 =20 =3Dpod =20 +=3Ditem string_replace_file(filename, find, replace) + +Find and replace string of a given file. + +=3Dcut + +sub string_replace_file +{ + my ($filename, $find, $replace) =3D @_; + open(my $in, '<', $filename); + my $content; + while(<$in>) + { + $_ =3D~ s/$find/$replace/; + $content =3D $content.$_; + } + close $in; + open(my $out, '>', $filename); + print $out $content; + close($out); + + return; +} + +=3Dpod + =3Ditem check_mode_recursive(dir, expected_dir_mode, expected_file_mode, i= gnore_list) =20 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/expecte= d/rules.out index 174b725fff..9596a673ad 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1759,7 +1759,7 @@ pg_stat_activity| SELECT s.datid, s.query_id, s.query, s.backend_type - FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, appl= ication_name, state, query, wait_event_type, wait_event, xact_start, query_= start, backend_start, state_change, client_addr, client_hostname, client_po= rt, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, ss= lbits, 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, appl= ication_name, state, query, wait_event_type, wait_event, xact_start, query_= start, backend_start, state_change, client_addr, client_hostname, client_po= rt, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, ss= lbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ= , gss_enc, gss_proxy, leader_pid, query_id) LEFT JOIN pg_database d ON ((s.datid =3D d.oid))) LEFT JOIN pg_authid u ON ((s.usesysid =3D u.oid))); pg_stat_all_indexes| SELECT c.oid AS relid, @@ -1873,8 +1873,9 @@ pg_stat_database_conflicts| SELECT oid AS datid, pg_stat_gssapi| SELECT pid, gss_auth AS gss_authenticated, gss_princ AS principal, - gss_enc AS encrypted - FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, applic= ation_name, state, query, wait_event_type, wait_event, xact_start, query_st= art, backend_start, state_change, client_addr, client_hostname, client_port= , backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslb= its, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, = gss_enc, leader_pid, query_id) + gss_enc AS encrypted, + gss_proxy AS credentials_proxied + FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, applic= ation_name, state, query, wait_event_type, wait_event, xact_start, query_st= art, backend_start, state_change, client_addr, client_hostname, client_port= , backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslb= its, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, = gss_enc, gss_proxy, leader_pid, query_id) WHERE (client_port IS NOT NULL); pg_stat_io| SELECT backend_type, io_object, @@ -2067,7 +2068,7 @@ pg_stat_replication| SELECT s.pid, w.sync_priority, w.sync_state, w.reply_time - FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, appl= ication_name, state, query, wait_event_type, wait_event, xact_start, query_= start, backend_start, state_change, client_addr, client_hostname, client_po= rt, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, ss= lbits, 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, appl= ication_name, state, query, wait_event_type, wait_event, xact_start, query_= start, backend_start, state_change, client_addr, client_hostname, client_po= rt, backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, ss= lbits, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ= , gss_enc, gss_proxy, leader_pid, query_id) JOIN pg_stat_get_wal_senders() w(pid, state, sent_lsn, write_lsn, flu= sh_lsn, replay_lsn, write_lag, flush_lag, replay_lag, sync_priority, sync_s= tate, reply_time) ON ((s.pid =3D w.pid))) LEFT JOIN pg_authid u ON ((s.usesysid =3D u.oid))); pg_stat_replication_slots| SELECT s.slot_name, @@ -2101,7 +2102,7 @@ pg_stat_ssl| SELECT pid, ssl_client_dn AS client_dn, ssl_client_serial AS client_serial, ssl_issuer_dn AS issuer_dn - FROM pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, applic= ation_name, state, query, wait_event_type, wait_event, xact_start, query_st= art, backend_start, state_change, client_addr, client_hostname, client_port= , backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslb= its, 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, applic= ation_name, state, query, wait_event_type, wait_event, xact_start, query_st= art, backend_start, state_change, client_addr, client_hostname, client_port= , backend_xid, backend_xmin, backend_type, ssl, sslversion, sslcipher, sslb= its, ssl_client_dn, ssl_client_serial, ssl_issuer_dn, gss_auth, gss_princ, = gss_enc, gss_proxy, leader_pid, query_id) WHERE (client_port IS NOT NULL); pg_stat_subscription| SELECT su.oid AS subid, su.subname, --yTvo40KjU/RXUJtp-- --XvWZoWEx23JIRy4T Content-Type: application/pgp-signature; name="signature.asc" -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEwf6gbxKhD863zrx/7WyKOINHZFUFAmPvSHgACgkQ7WyKOINH ZFWVbhAAiZpNeIdpLJs855+eWpWg4Qqqr6Hg3lNanisy6bTfuAK7u4K7M/n/8ePQ xJRAjwelEiEpSmvB/R6HeL1+fH2Uw+Q4/w0LAtGv1qHPekUb8LToUGRY/qxOew43 mho6xbBxbyvc0Mad6PVKcQFxCVIAKHKRQWEEtR8rVvAWXja2/ZMCNBPrIm0y+02E iFd9or4lhTpg1xdLHWD6MWG4x1+fQKbbKzgsbJuk2mrKqGNj0AUlxAuSStTKQTAO 5IH2uuliKRceVjDBdd7mFLZcyqy90f02Ji1NRYYsQ+NQYxoP1D1veNvVn4J5vlYX n6gvGWSr+JKjBWYA4/9v3Y5cVufy11IDndzY/mfzqnfaeDTZM5/+jmdxML2geCm5 +Fp3s4x/QONXNvD4utrj7ILWuDzmD8Ny+dNU2cSXXduyx1jTYXiz1YRosE/UxGiI cQlB4ESp5bEwm3zW2WXZ82ngVOZqF+u/kH4CXVOoopbE9huTMTMltCAmjsMxVMbk qtgsdDg5u85Qwl1g/Yr8BUHxfJU3hkW4AUnTxkOBY2bt8gdPoPedUzxhELy5JSxF gaafk9YwAza+6Ripa7EF+RYhLZYPo2KnR7mgfUv929Q7HEE8Bb+9t0x/iXAom7yg StIT8OkjBdbS4kn6bM5ofihI05Wb5/+PcsgmRTLhPUzYFQAr7ng= =ydB7 -----END PGP SIGNATURE----- --XvWZoWEx23JIRy4T--