Greetinsg, * Jacob Champion (pchamp...@vmware.com) wrote: > On Fri, 2022-03-11 at 19:39 -0500, Stephen Frost wrote: > > On Fri, Mar 11, 2022 at 18:55 Jacob Champion <pchamp...@vmware.com> wrote: > > > [5] says we have to free the proxy credential with GSS_Release_cred(); > > > I don't see that happening anywhere, but I may have missed it. > > > > I’m not sure that it’s really necessary or worthwhile to do that at > > process end since … the process is about to end. I suppose we could > > provide a function that a user could call to ask for it to be > > released sooner if we really wanted..? > > Do we have to keep the credential handle around once we've stored it > into the MEMORY: cache, though? Just seems like a leak that someone > will have to plug eventually, even if it doesn't really impact things > now.
We don't, so I've fixed that in the attached. Not sure it's that big a deal but I don't think it hurts anything either. > > > It seems like there should be significant security implications to > > > allowing delegation across the board. Especially since one postgres_fdw > > > might delegate to another server, and then another... Should this be > > > opt-in, maybe via a connection parameter? > > > > This is already opt-in- at kinit time a user can decide if they’d > > like a proxy-able ticket or not. I don’t know that we really need to > > have our own option for it … tho I’m not really against adding such > > an option either. > > I don't really have experience with the use case. Is it normal for > kinit users to have to decide once, globally, whether they want > everything they interact with to be able to proxy their credentials? It > just seems like you'd want more fine-grained control over who gets to > masquerade as you. Yes, that's pretty typical for kinit users- they usually go with whatever the org policy is. Now, you're not wrong about wanting more fine-grained control, which is what's known as 'constrained delegation'. That's something that Kerberos in general supports these days though it's more complicated and requires additional code to do. That's something that I think we could certainly add later on. > > > Similarly, it feels a little strange that the server would allow the > > > client to unilaterally force the use of a delegated credential. I think > > > that should be opt-in on the server side too, unless there's some > > > context I'm missing around why that's safe. > > > > Perhaps you could explain what isn’t safe about accepting a delegated > > credential from a client..? I am not away of a risk to accepting > > such a delegated credential. > > My initial impression is that this is effectively modifying the USER > MAPPING that the admin has set up. I'd be worried about an open > credential proxy being used to bypass firewall or HBA restrictions, for > instance -- you might not be able to connect as an admin from your > machine, but you might be able to connect by bouncing through a proxy. > (What damage you can do is going to be limited by what the server > extensions can do, of course.) I'm not sure that I really see the concern here. Also, in order for this to work, the user mapping would have to be created with "password required = false". Maybe that's something we revisit later, but it seems like a good way to allow an admin to have control over this. > Another danger might be disclosure/compromise of middlebox secrets? Is > it possible for someone who has one half of the credentials to snoop on > a gssenc connection between the proxy Postgres and the backend > Postgres? A compromised middlebox would, of course, be an issue- for any kind of delegated credentials (which certainly goes for cleartext passwords being passed along, and that's currently the only thing we support..). One nice thing about GSSAPI is that the client and the server validate each other, so it wouldn't just be 'any' middle-box but would have to be one that was actually a trusted system in the infrastructure which has somehow been compromised and was still trusted. > > Even so, I’m not against adding an option… but exactly how would that > > option be configured? Server level? On the HBA line? role level..? > > In the OPTIONS for CREATE SERVER, maybe? At least for the FDW case. I'm a bit confused on this. The option to allow or not allow delegated credentials couldn't be something that's in the CREATE SERVER for FDWs as it applies to more than just FDWs but also dblink and anything else where we reach out from PG to contact some other system. > > > If I'm reading it right, we're resetting the default credential in the > > > MEMORY cache, so if you're a libpq client doing your own GSSAPI work, > > > I'm guessing you might not be happy with this behavior. > > > > This is just done on the server side and not the client side..? > > Yeah, I misread the patch, sorry. No worries. > > > Also, we're > > > globally ignoring whatever ccache was set by an administrator. Can't > > > two postgres_fdw connections from the same backend process require > > > different settings? > > > > Settings..? Perhaps, but delegated credentials aren’t really > > settings, so not really sure what you’re suggesting here. > > I mean that one backend server might require delegated credentials, and > another might require whatever the admin has already set up in the > ccache, and the user might want to use tables from both servers in the > same session. That an admin might have a credential cache that's picked up and used for connections from a regular user backend to another system strikes me as an altogether concerning idea. Even so, in such a case, the admin would have had to set up the user mapping with 'password required = false' or it wouldn't have worked for a non-superuser anyway, so I'm not sure that I'm too worried about this case. > > > I notice that gss_store_cred_into() has a companion, > > > gss_acquire_cred_from(). Is it possible to use that to pull out our > > > delegated credential explicitly by name, instead of stomping on the > > > global setup? > > > > Not really sure what is meant here by global setup..? Feeling like > > this is a follow on confusion from maybe mixing server vs client > > libpq? > > By my reading, the gss_store_cred_into() call followed by > the setenv("KRB5CCNAME", ...) is effectively performing global > configuration for the process. Any KRB5CCNAME already set up by the > server admin is going to be ignored from that point onward. Is that > accurate? The process, yes, but I guess I disagree on that being 'global'- it's just for that PG backend process. Attached is an updated patch which adds the gss_release_creds call, a function in libpq to allow checking if the libpq connection was made using GSS, changes to dblink to have it check for password-or-gss when connecting to a remote system, and tests for dblink and postgres_fdw to make sure that this all works correctly. Thoughts? Thanks! Stephen
From b9aaf1705d9de6ac9e95d97980551ac27343bcab Mon Sep 17 00:00:00 2001 From: Stephen Frost <sfr...@snowman.net> Date: Mon, 28 Feb 2022 20:17:55 -0500 Subject: [PATCH] Add support for Kerberos credential delegation Accept GSSAPI/Kerberos delegated credentials. With this, a user could authenticate to PostgreSQL using Kerberos credentials, delegate credentials to the PostgreSQL server, and then the PostgreSQL server could use those credentials to connect to another service, such as with postgres_fdw or dblink or theoretically any other authenticated connection which is able to use delegated credentials. Original patch by: Peifeng Qiu, whacked around some by me. --- contrib/dblink/dblink.c | 6 +- .../postgres_fdw/expected/postgres_fdw.out | 2 +- contrib/postgres_fdw/option.c | 3 + doc/src/sgml/libpq.sgml | 19 +++++ src/backend/libpq/auth.c | 12 +++- src/backend/libpq/be-gssapi-common.c | 51 +++++++++++++ src/backend/libpq/be-secure-gssapi.c | 26 ++++++- src/backend/utils/init/postinit.c | 8 ++- src/include/libpq/be-gssapi-common.h | 3 + src/include/libpq/libpq-be.h | 2 + src/interfaces/libpq/exports.txt | 1 + src/interfaces/libpq/fe-auth.c | 12 +++- src/interfaces/libpq/fe-connect.c | 12 ++++ src/interfaces/libpq/fe-secure-gssapi.c | 3 +- src/interfaces/libpq/libpq-fe.h | 1 + src/interfaces/libpq/libpq-int.h | 1 + src/test/kerberos/Makefile | 3 + src/test/kerberos/t/001_auth.pl | 71 ++++++++++++++++--- src/test/perl/PostgreSQL/Test/Utils.pm | 27 +++++++ 19 files changed, 243 insertions(+), 20 deletions(-) diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c index a06d4bd12d..e5b70e084e 100644 --- a/contrib/dblink/dblink.c +++ b/contrib/dblink/dblink.c @@ -2643,7 +2643,7 @@ dblink_security_check(PGconn *conn, remoteConn *rconn) { if (!superuser()) { - if (!PQconnectionUsedPassword(conn)) + if (!(PQconnectionUsedPassword(conn) || PQconnectionUsedGSSAPI(conn))) { PQfinish(conn); ReleaseExternalFD(); @@ -2652,8 +2652,8 @@ dblink_security_check(PGconn *conn, remoteConn *rconn) ereport(ERROR, (errcode(ERRCODE_S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED), - errmsg("password is required"), - errdetail("Non-superuser cannot connect if the server does not request a password."), + errmsg("password or GSSAPI is required"), + errdetail("Non-superuser cannot connect if the server does not request a password or use GSSAPI."), errhint("Target server's authentication method must be changed."))); } } diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index 30e95f585f..d472c24601 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -188,7 +188,7 @@ ALTER USER MAPPING FOR public SERVER testserver1 ALTER USER MAPPING FOR public SERVER testserver1 OPTIONS (ADD sslmode 'require'); ERROR: invalid option "sslmode" -HINT: Valid options in this context are: user, password, sslpassword, password_required, sslcert, sslkey +HINT: Valid options in this context are: user, password, sslpassword, password_required, sslcert, sslkey, gssencmode -- But we can add valid ones fine ALTER USER MAPPING FOR public SERVER testserver1 OPTIONS (ADD sslpassword 'dummy'); diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c index 572591a558..05922cfe6d 100644 --- a/contrib/postgres_fdw/option.c +++ b/contrib/postgres_fdw/option.c @@ -262,6 +262,9 @@ InitPgFdwOptions(void) {"sslcert", UserMappingRelationId, true}, {"sslkey", UserMappingRelationId, true}, + /* gssencmode is also libpq option, same to above. */ + {"gssencmode", UserMappingRelationId, true}, + {NULL, InvalidOid, false} }; diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index 1c20901c3c..22ba41ea10 100644 --- a/doc/src/sgml/libpq.sgml +++ b/doc/src/sgml/libpq.sgml @@ -2489,6 +2489,25 @@ int PQconnectionUsedPassword(const PGconn *conn); </para> </listitem> </varlistentry> + + <varlistentry id="libpq-PQconnectionUsedGSSAPI"> + <term><function>PQconnectionUsedGSSAPI</function><indexterm><primary>PQconnectionUsedGSSAPI</primary></indexterm></term> + <listitem> + <para> + Returns true (1) if the connection authentication method + used GSSAPI. Returns false (0) if not. + +<synopsis> +int PQconnectionUsedGSSAPI(const PGconn *conn); +</synopsis> + </para> + + <para> + This function can be applied to detect whether the connection was + authenticated with GSSAPI. + </para> + </listitem> + </varlistentry> </variablelist> </para> diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c index efc53f3135..6f820a34f1 100644 --- a/src/backend/libpq/auth.c +++ b/src/backend/libpq/auth.c @@ -920,6 +920,7 @@ pg_GSS_recvauth(Port *port) int mtype; StringInfoData buf; gss_buffer_desc gbuf; + gss_cred_id_t proxy; /* * Use the configured keytab, if there is one. Unfortunately, Heimdal @@ -949,6 +950,9 @@ pg_GSS_recvauth(Port *port) */ port->gss->ctx = GSS_C_NO_CONTEXT; + proxy = NULL; + port->gss->proxy_creds = false; + /* * Loop through GSSAPI message exchange. This exchange can consist of * multiple messages sent in both directions. First message is always from @@ -999,7 +1003,7 @@ pg_GSS_recvauth(Port *port) &port->gss->outbuf, &gflags, NULL, - NULL); + &proxy); /* gbuf no longer used */ pfree(buf.data); @@ -1011,6 +1015,12 @@ pg_GSS_recvauth(Port *port) CHECK_FOR_INTERRUPTS(); + if (proxy != NULL) + { + pg_store_proxy_credential(proxy); + port->gss->proxy_creds = true; + } + if (port->gss->outbuf.length != 0) { /* diff --git a/src/backend/libpq/be-gssapi-common.c b/src/backend/libpq/be-gssapi-common.c index 71b796d5a2..9e7e7b5c85 100644 --- a/src/backend/libpq/be-gssapi-common.c +++ b/src/backend/libpq/be-gssapi-common.c @@ -92,3 +92,54 @@ pg_GSS_error(const char *errmsg, (errmsg_internal("%s", errmsg), errdetail_internal("%s: %s", msg_major, msg_minor))); } + +/* + * Store the credentials passed in into the memory cache for later usage. + * + * This allows credentials to be delegated to us for us to use to connect + * to other systems with, using, e.g. postgres_fdw or dblink. + */ +#define GSS_MEMORY_CACHE "MEMORY:" +void +pg_store_proxy_credential(gss_cred_id_t cred) +{ + OM_uint32 major, minor; + gss_OID_set mech; + gss_cred_usage_t usage; + gss_key_value_element_desc cc; + gss_key_value_set_desc ccset; + + cc.key = "ccache"; + cc.value = GSS_MEMORY_CACHE; + ccset.count = 1; + ccset.elements = &cc; + + /* Make the proxy credential only available to current process */ + major = gss_store_cred_into(&minor, + cred, + GSS_C_INITIATE, /* credential only used for starting libpq connection */ + GSS_C_NULL_OID, /* store all */ + true, /* overwrite */ + true, /* make default */ + &ccset, + &mech, + &usage); + + if (major != GSS_S_COMPLETE) + { + pg_GSS_error("gss_store_cred", major, minor); + } + + /* Credential stored, so we can release our credential handle. */ + major = gss_release_cred(&minor, &cred); + if (major != GSS_S_COMPLETE) + { + pg_GSS_error("gss_release_cred", major, minor); + } + + /* + * Set KRB5CCNAME for this backend, so that later calls to gss_acquire_cred + * will find the proxied credentials we stored. + */ + setenv("KRB5CCNAME", GSS_MEMORY_CACHE, 1); +} diff --git a/src/backend/libpq/be-secure-gssapi.c b/src/backend/libpq/be-secure-gssapi.c index 2844c5aa4b..bece580f61 100644 --- a/src/backend/libpq/be-secure-gssapi.c +++ b/src/backend/libpq/be-secure-gssapi.c @@ -497,6 +497,7 @@ secure_open_gssapi(Port *port) bool complete_next = false; OM_uint32 major, minor; + gss_cred_id_t proxy; /* * Allocate subsidiary Port data for GSSAPI operations. @@ -504,6 +505,9 @@ secure_open_gssapi(Port *port) port->gss = (pg_gssinfo *) MemoryContextAllocZero(TopMemoryContext, sizeof(pg_gssinfo)); + proxy = NULL; + port->gss->proxy_creds = false; + /* * Allocate buffers and initialize state variables. By malloc'ing the * buffers at this point, we avoid wasting static data space in processes @@ -588,7 +592,8 @@ secure_open_gssapi(Port *port) GSS_C_NO_CREDENTIAL, &input, GSS_C_NO_CHANNEL_BINDINGS, &port->gss->name, NULL, &output, NULL, - NULL, NULL); + NULL, &proxy); + if (GSS_ERROR(major)) { pg_GSS_error(_("could not accept GSSAPI security context"), @@ -605,6 +610,12 @@ secure_open_gssapi(Port *port) complete_next = true; } + if (proxy != NULL) + { + pg_store_proxy_credential(proxy); + port->gss->proxy_creds = true; + } + /* Done handling the incoming packet, reset our buffer */ PqGSSRecvLength = 0; @@ -731,3 +742,16 @@ be_gssapi_get_princ(Port *port) return port->gss->princ; } + +/* + * Return if GSSAPI delegated/proxy credentials were included on this + * connection. + */ +bool +be_gssapi_get_proxy(Port *port) +{ + if (!port || !port->gss) + return NULL; + + return port->gss->proxy_creds; +} diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c index 342169b195..27b3404b86 100644 --- a/src/backend/utils/init/postinit.c +++ b/src/backend/utils/init/postinit.c @@ -284,15 +284,17 @@ PerformAuthentication(Port *port) if (princ) appendStringInfo(&logmsg, - _(" GSS (authenticated=%s, encrypted=%s, principal=%s)"), + _(" GSS (authenticated=%s, encrypted=%s, proxy_credentials=%s, principal=%s)"), be_gssapi_get_auth(port) ? _("yes") : _("no"), be_gssapi_get_enc(port) ? _("yes") : _("no"), + be_gssapi_get_proxy(port) ? _("yes") : _("no"), princ); else appendStringInfo(&logmsg, - _(" GSS (authenticated=%s, encrypted=%s)"), + _(" GSS (authenticated=%s, encrypted=%s, proxy_credentials=%s)"), be_gssapi_get_auth(port) ? _("yes") : _("no"), - be_gssapi_get_enc(port) ? _("yes") : _("no")); + be_gssapi_get_enc(port) ? _("yes") : _("no"), + be_gssapi_get_proxy(port) ? _("yes") : _("no")); } #endif diff --git a/src/include/libpq/be-gssapi-common.h b/src/include/libpq/be-gssapi-common.h index ae8411245d..6953157f05 100644 --- a/src/include/libpq/be-gssapi-common.h +++ b/src/include/libpq/be-gssapi-common.h @@ -18,13 +18,16 @@ #if defined(HAVE_GSSAPI_H) #include <gssapi.h> +#include <gssapi_ext.h> #else #include <gssapi/gssapi.h> +#include <gssapi/gssapi_ext.h> #endif extern void pg_GSS_error(const char *errmsg, OM_uint32 maj_stat, OM_uint32 min_stat); +extern void pg_store_proxy_credential(gss_cred_id_t cred); #endif /* ENABLE_GSS */ #endif /* BE_GSSAPI_COMMON_H */ diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h index c3bf514652..5cc221617e 100644 --- a/src/include/libpq/libpq-be.h +++ b/src/include/libpq/libpq-be.h @@ -94,6 +94,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 @@ -320,6 +321,7 @@ extern PGDLLIMPORT openssl_tls_init_hook_typ openssl_tls_init_hook; extern bool be_gssapi_get_auth(Port *port); extern bool be_gssapi_get_enc(Port *port); extern const char *be_gssapi_get_princ(Port *port); +extern bool be_gssapi_get_proxy(Port *port); /* Read and write to a GSSAPI-encrypted connection. */ extern ssize_t be_gssapi_read(Port *port, void *ptr, size_t len); diff --git a/src/interfaces/libpq/exports.txt b/src/interfaces/libpq/exports.txt index e8bcc88370..7ded77aff3 100644 --- a/src/interfaces/libpq/exports.txt +++ b/src/interfaces/libpq/exports.txt @@ -186,3 +186,4 @@ PQpipelineStatus 183 PQsetTraceFlags 184 PQmblenBounded 185 PQsendFlushRequest 186 +PQconnectionUsedGSSAPI 187 diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c index 6fceff561b..943db5c722 100644 --- a/src/interfaces/libpq/fe-auth.c +++ b/src/interfaces/libpq/fe-auth.c @@ -61,6 +61,7 @@ pg_GSS_continue(PGconn *conn, int payloadlen) lmin_s; gss_buffer_desc ginbuf; gss_buffer_desc goutbuf; + gss_cred_id_t proxy; /* * On first call, there's no input token. On subsequent calls, read the @@ -93,12 +94,16 @@ pg_GSS_continue(PGconn *conn, int payloadlen) ginbuf.value = NULL; } + /* Check if we can aquire a proxy credential. */ + if (!pg_GSS_have_cred_cache(&proxy)) + proxy = GSS_C_NO_CREDENTIAL; + maj_stat = gss_init_sec_context(&min_stat, - GSS_C_NO_CREDENTIAL, + proxy, &conn->gctx, conn->gtarg_nam, GSS_C_NO_OID, - GSS_C_MUTUAL_FLAG, + GSS_C_MUTUAL_FLAG | GSS_C_DELEG_FLAG, 0, GSS_C_NO_CHANNEL_BINDINGS, (ginbuf.value == NULL) ? GSS_C_NO_BUFFER : &ginbuf, @@ -138,7 +143,10 @@ pg_GSS_continue(PGconn *conn, int payloadlen) } if (maj_stat == GSS_S_COMPLETE) + { gss_release_name(&lmin_s, &conn->gtarg_nam); + conn->gssapi_used = true; + } return STATUS_OK; } diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index cf554d389f..a296c82d1d 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -600,6 +600,7 @@ pqDropServerData(PGconn *conn) conn->last_sqlstate[0] = '\0'; conn->auth_req_received = false; conn->password_needed = false; + conn->gssapi_used = false; conn->write_failed = false; if (conn->write_err_msg) free(conn->write_err_msg); @@ -6981,6 +6982,17 @@ PQconnectionUsedPassword(const PGconn *conn) return false; } +int +PQconnectionUsedGSSAPI(const PGconn *conn) +{ + if (!conn) + return false; + if (conn->gssapi_used) + return true; + else + return false; +} + int PQclientEncoding(const PGconn *conn) { diff --git a/src/interfaces/libpq/fe-secure-gssapi.c b/src/interfaces/libpq/fe-secure-gssapi.c index 6ea52ed866..5eeaca542d 100644 --- a/src/interfaces/libpq/fe-secure-gssapi.c +++ b/src/interfaces/libpq/fe-secure-gssapi.c @@ -631,7 +631,7 @@ pqsecure_open_gss(PGconn *conn) */ 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_REQUIRED_FLAGS | GSS_C_DELEG_FLAG, 0, 0, &input, NULL, &output, NULL, NULL); /* GSS Init Sec Context uses the whole packet, so clear it */ @@ -651,6 +651,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 7986445f1a..bdd073c645 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 e0cee4b142..5dd9a52305 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -449,6 +449,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 c531998835..67dfaae901 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 62e0542639..c5dc552d15 100644 --- a/src/test/kerberos/t/001_auth.pl +++ b/src/test/kerberos/t/001_auth.pl @@ -45,6 +45,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'; @@ -53,6 +54,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) { @@ -97,6 +99,7 @@ kdc = FILE:$kdc_log [libdefaults] default_realm = $realm +forwardable = false [realms] $realm = { @@ -174,7 +177,21 @@ lc_messages = 'C' }); $node->start; +my $port = $node->port(); + $node->safe_psql('postgres', 'CREATE USER test1;'); +$node->safe_psql('postgres', 'CREATE EXTENSION postgres_fdw;'); +$node->safe_psql('postgres', 'CREATE EXTENSION dblink;'); +$node->safe_psql('postgres', "CREATE SERVER s1 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '$host', hostaddr '$hostaddr', port '$port', dbname 'postgres');"); + +$node->safe_psql('postgres', 'GRANT USAGE ON FOREIGN SERVER s1 TO test1;'); + +$node->safe_psql('postgres', "CREATE USER MAPPING FOR test1 SERVER s1 OPTIONS (user 'test1', password_required 'false');"); +$node->safe_psql('postgres', "CREATE TABLE t1 (c1 int);"); +$node->safe_psql('postgres', "INSERT INTO t1 VALUES (1);"); +$node->safe_psql('postgres', "CREATE FOREIGN TABLE tf1 (c1 int) SERVER s1 OPTIONS (schema_name 'public', table_name 't1');"); +$node->safe_psql('postgres', "GRANT SELECT ON t1 TO test1;"); +$node->safe_psql('postgres', "GRANT SELECT ON tf1 TO test1;"); note "running tests"; @@ -240,6 +257,7 @@ $node->restart; test_access($node, 'test1', 'SELECT true', 2, '', 'fails without ticket'); run_log [ $kinit, 'test1' ], \$test1_password or BAIL_OUT($?); +run_log [ $klist, '-f' ] or BAIL_OUT($?); test_access( $node, @@ -262,7 +280,7 @@ test_access( '', 'succeeds with mapping with default gssencmode and host hba', "connection authenticated: identity=\"test1\@$realm\" method=gss", - "connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, principal=test1\@$realm)" + "connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, proxy_credentials=no, principal=test1\@$realm)" ); test_access( @@ -273,7 +291,7 @@ test_access( 'gssencmode=prefer', 'succeeds with GSS-encrypted access preferred with host hba', "connection authenticated: identity=\"test1\@$realm\" method=gss", - "connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, principal=test1\@$realm)" + "connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, proxy_credentials=no, principal=test1\@$realm)" ); test_access( $node, @@ -283,7 +301,7 @@ test_access( 'gssencmode=require', 'succeeds with GSS-encrypted access required with host hba', "connection authenticated: identity=\"test1\@$realm\" method=gss", - "connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, principal=test1\@$realm)" + "connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, proxy_credentials=no, principal=test1\@$realm)" ); # Test that we can transport a reasonable amount of data. @@ -312,6 +330,11 @@ $node->append_conf('pg_hba.conf', qq{hostgssenc all all $hostaddr/32 gss map=mymap}); $node->restart; +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', @@ -320,7 +343,7 @@ test_access( 'gssencmode=prefer', 'succeeds with GSS-encrypted access preferred and hostgssenc hba', "connection authenticated: identity=\"test1\@$realm\" method=gss", - "connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, principal=test1\@$realm)" + "connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, proxy_credentials=yes, principal=test1\@$realm)" ); test_access( $node, @@ -330,7 +353,7 @@ test_access( 'gssencmode=require', 'succeeds with GSS-encrypted access required and hostgssenc hba', "connection authenticated: identity=\"test1\@$realm\" method=gss", - "connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, principal=test1\@$realm)" + "connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, proxy_credentials=yes, principal=test1\@$realm)" ); test_access($node, 'test1', 'SELECT true', 2, 'gssencmode=disable', 'fails with GSS encryption disabled and hostgssenc hba'); @@ -348,7 +371,7 @@ test_access( 'gssencmode=prefer', 'succeeds with GSS-encrypted access preferred and hostnogssenc hba, but no encryption', "connection authenticated: identity=\"test1\@$realm\" method=gss", - "connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=no, principal=test1\@$realm)" + "connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=no, proxy_credentials=yes, principal=test1\@$realm)" ); test_access($node, 'test1', 'SELECT true', 2, 'gssencmode=require', 'fails with GSS-encrypted access required and hostnogssenc hba'); @@ -360,9 +383,25 @@ test_access( 'gssencmode=disable', 'succeeds with GSS encryption disabled and hostnogssenc hba', "connection authenticated: identity=\"test1\@$realm\" method=gss", - "connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=no, principal=test1\@$realm)" + "connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=no, proxy_credentials=yes, principal=test1\@$realm)" ); +test_query( + $node, + 'test1', + "SELECT * FROM dblink('user=test1 dbname=$dbname host=$host hostaddr=$hostaddr port=$port password=1234','select 1') as t1(c1 int);", + qr/^1$/s, + 'gssencmode=prefer', + 'dblink works not-encrypted'); + +test_query( + $node, + 'test1', + "TABLE tf1;", + qr/^1$/s, + 'gssencmode=prefer', + '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', @@ -377,9 +416,25 @@ test_access( '', 'succeeds with include_realm=0 and defaults', "connection authenticated: identity=\"test1\@$realm\" method=gss", - "connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, principal=test1\@$realm)" + "connection authorized: user=$username database=$dbname application_name=$application GSS (authenticated=yes, encrypted=yes, proxy_credentials=yes, principal=test1\@$realm)" ); +test_query( + $node, + 'test1', + "SELECT * FROM dblink('user=test1 dbname=$dbname host=$host hostaddr=$hostaddr port=$port password=1234','select 1') as t1(c1 int);", + qr/^1$/s, + 'gssencmode=require', + 'dblink works encrypted'); + +test_query( + $node, + 'test1', + "TABLE tf1;", + qr/^1$/s, + 'gssencmode=require', + 'postgres_fdw works encrypted'); + # Reset pg_hba.conf, and cause a usermap failure with an authentication # that has passed. unlink($node->data_dir . '/pg_hba.conf'); diff --git a/src/test/perl/PostgreSQL/Test/Utils.pm b/src/test/perl/PostgreSQL/Test/Utils.pm index dca1b3b17c..0f94a628a5 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 @@ -544,6 +545,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, -- 2.32.0
signature.asc
Description: PGP signature