diff --git a/contrib/postgres_fdw/Makefile b/contrib/postgres_fdw/Makefile index c1b0cad453..2113fa8028 100644 --- a/contrib/postgres_fdw/Makefile +++ b/contrib/postgres_fdw/Makefile @@ -7,7 +7,8 @@ OBJS = \ deparse.o \ option.o \ postgres_fdw.o \ - shippable.o + shippable.o \ + gss_proxy.o PGFILEDESC = "postgres_fdw - foreign data wrapper for PostgreSQL" PG_CPPFLAGS = -I$(libpq_srcdir) diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c index 82aa14a65d..e5e5f534ae 100644 --- a/contrib/postgres_fdw/connection.c +++ b/contrib/postgres_fdw/connection.c @@ -333,6 +333,9 @@ make_new_connection(ConnCacheEntry *entry, UserMapping *user) entry->conn, server->servername, user->umid, user->userid); } +extern char* get_gss_proxy_cred(); +extern bool has_gss_proxy_cred(); + /* * Connect to remote server using specified server and user mapping properties. */ @@ -349,14 +352,16 @@ connect_pg_server(ForeignServer *server, UserMapping *user) const char **keywords; const char **values; int n; + char *gss_proxy_cred_addr; /* * Construct connection params from generic options of ForeignServer * and UserMapping. (Some of them might not be libpq options, in - * which case we'll just waste a few array slots.) Add 3 extra slots - * for fallback_application_name, client_encoding, end marker. + * which case we'll just waste a few array slots.) Add 4 extra slots + * for fallback_application_name, client_encoding, end marker, + * gss_proxy_cred. */ - n = list_length(server->options) + list_length(user->options) + 3; + n = list_length(server->options) + list_length(user->options) + 4; keywords = (const char **) palloc(n * sizeof(char *)); values = (const char **) palloc(n * sizeof(char *)); @@ -376,6 +381,14 @@ connect_pg_server(ForeignServer *server, UserMapping *user) values[n] = GetDatabaseEncodingName(); n++; + gss_proxy_cred_addr = get_gss_proxy_cred(); + if (gss_proxy_cred_addr != NULL) + { + keywords[n] = "gss_proxy_cred"; + values[n] = gss_proxy_cred_addr; + n++; + } + keywords[n] = values[n] = NULL; /* verify the set of connection parameters */ @@ -476,6 +489,9 @@ UserMappingPasswordRequired(UserMapping *user) { ListCell *cell; + if (has_gss_proxy_cred()) + return false; + foreach(cell, user->options) { DefElem *def = (DefElem *) lfirst(cell); diff --git a/contrib/postgres_fdw/gss_proxy.c b/contrib/postgres_fdw/gss_proxy.c new file mode 100644 index 0000000000..1f016c54fc --- /dev/null +++ b/contrib/postgres_fdw/gss_proxy.c @@ -0,0 +1,25 @@ +#include + +#include "postgres.h" +#include "miscadmin.h" +#include "libpq/libpq-be.h" + +bool has_gss_proxy_cred() +{ + return MyProcPort->gss != NULL && MyProcPort->gss->proxy != NULL; +} + +/* ugly hack to pass the gss proxy credential from backend Port to frontend libpq. */ +char* get_gss_proxy_cred() +{ + char* addr = NULL; + if (MyProcPort->gss) + { + if (MyProcPort->gss->proxy) + { + addr = palloc(sizeof(void*) + 3); + sprintf(addr, "%p", MyProcPort->gss->proxy); + } + } + return addr; +} diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c index 8cc23ef7fb..2cb3e3b435 100644 --- a/src/backend/libpq/auth.c +++ b/src/backend/libpq/auth.c @@ -935,12 +935,17 @@ pg_GSS_recvauth(Port *port) } /* - * We accept any service principal that's present in our keytab. This - * increases interoperability between kerberos implementations that see - * for example case sensitivity differently, while not really opening up - * any vector of attack. + * Acquire default credential with GSS_C_BOTH. Comprare to using + * GSS_C_NO_CREDENTIAL, this allows us to also acquire a proxy + * credential, which can be used in postgres_fdw as for delegation. */ - port->gss->cred = GSS_C_NO_CREDENTIAL; + maj_stat = gss_acquire_cred(&min_stat, GSS_C_NO_NAME, 0, + NULL, GSS_C_BOTH, &port->gss->cred, NULL, NULL); + if (maj_stat != GSS_S_COMPLETE) + { + pg_GSS_error(_("gss_acquire_cred failed"), maj_stat, min_stat); + return STATUS_ERROR; + } /* * Initialize sequence with an empty context @@ -997,7 +1002,7 @@ pg_GSS_recvauth(Port *port) &port->gss->outbuf, &gflags, NULL, - NULL); + &port->gss->proxy); /* gbuf no longer used */ pfree(buf.data); diff --git a/src/backend/libpq/be-secure-gssapi.c b/src/backend/libpq/be-secure-gssapi.c index 316ca65db5..4a130f8d0f 100644 --- a/src/backend/libpq/be-secure-gssapi.c +++ b/src/backend/libpq/be-secure-gssapi.c @@ -536,6 +536,19 @@ secure_open_gssapi(Port *port) } } + /* + * Acquire default credential with GSS_C_BOTH. Comprare to using + * GSS_C_NO_CREDENTIAL, this allows us to also acquire a proxy + * credential, which can be used in postgres_fdw as for delegation. + */ + major = gss_acquire_cred(&minor, GSS_C_NO_NAME, 0, + NULL, GSS_C_BOTH, &port->gss->cred, NULL, NULL); + if (major != GSS_S_COMPLETE) + { + pg_GSS_error(_("gss_acquire_cred failed"), major, minor); + return -1; + } + while (true) { ssize_t ret; @@ -585,10 +598,10 @@ secure_open_gssapi(Port *port) /* Process incoming data. (The client sends first.) */ major = gss_accept_sec_context(&minor, &port->gss->ctx, - GSS_C_NO_CREDENTIAL, &input, + port->gss->cred, &input, GSS_C_NO_CHANNEL_BINDINGS, &port->gss->name, NULL, &output, NULL, - NULL, NULL); + NULL, &port->gss->proxy); if (GSS_ERROR(major)) { pg_GSS_error(_("could not accept GSSAPI security context"), diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h index 02015efe13..6efaf75bfd 100644 --- a/src/include/libpq/libpq-be.h +++ b/src/include/libpq/libpq-be.h @@ -95,6 +95,8 @@ typedef struct * GSSAPI auth was not used */ bool auth; /* GSSAPI Authentication used */ bool enc; /* GSSAPI encryption in use */ + + gss_cred_id_t proxy; #endif } pg_gssinfo; #endif diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c index 3421ed4685..70da371366 100644 --- a/src/interfaces/libpq/fe-auth.c +++ b/src/interfaces/libpq/fe-auth.c @@ -62,6 +62,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,9 +94,15 @@ pg_GSS_continue(PGconn *conn, int payloadlen) ginbuf.length = 0; ginbuf.value = NULL; } + proxy = GSS_C_NO_CREDENTIAL; + if (conn->gssproxycred) + { + if (1 != sscanf(conn->gssproxycred, "%p", &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, @@ -1307,4 +1314,4 @@ PQencryptPasswordConn(PGconn *conn, const char *passwd, const char *user, libpq_gettext("out of memory\n")); return crypt_pwd; -} +} \ No newline at end of file diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index e950b41374..25ac862dc6 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -335,6 +335,10 @@ static const internalPQconninfoOption PQconninfoOptions[] = { "GSS-library", "", 7, /* sizeof("gssapi") == 7 */ offsetof(struct pg_conn, gsslib)}, + {"gss_proxy_cred", "PGGSSPROXYCRED", NULL, NULL, + "GSS proxy credential", "", 19, /* sizeof("0x0123456789abcdef") */ + offsetof(struct pg_conn, gssproxycred)}, + {"replication", NULL, NULL, NULL, "Replication", "D", 5, offsetof(struct pg_conn, replication)}, @@ -4116,6 +4120,8 @@ freePGconn(PGconn *conn) free(conn->krbsrvname); if (conn->gsslib) free(conn->gsslib); + if (conn->gssproxycred) + free(conn->gssproxycred); if (conn->connip) free(conn->connip); /* Note that conn->Pfdebug is not ours to close or free */ diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index e9f214b61b..120d13f03f 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -390,6 +390,7 @@ struct pg_conn char *krbsrvname; /* Kerberos service name */ char *gsslib; /* What GSS library to use ("gssapi" or * "sspi") */ + char *gssproxycred; /* proxy/delegation credential address(hex string) */ 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 */