1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-21 00:42:43 +03:00

Add support for Kerberos credential delegation

Support GSSAPI/Kerberos credentials being delegated to the server by a
client.  With this, a user authenticating to PostgreSQL using Kerberos
(GSSAPI) credentials can choose to delegate their credentials to the
PostgreSQL server (which can choose to accept them, or not), allowing
the server to then use those delegated credentials to connect to
another service, such as with postgres_fdw or dblink or theoretically
any other service which is able to be authenticated using Kerberos.

Both postgres_fdw and dblink are changed to allow non-superuser
password-less connections but only when GSSAPI credentials have been
delegated to the server by the client and GSSAPI is used to
authenticate to the remote system.

Authors: Stephen Frost, Peifeng Qiu
Reviewed-By: David Christensen
Discussion: https://postgr.es/m/CO1PR05MB8023CC2CB575E0FAAD7DF4F8A8E29@CO1PR05MB8023.namprd05.prod.outlook.com
This commit is contained in:
Stephen Frost
2023-04-07 21:58:04 -04:00
parent edc627ae27
commit 3d4fa227bc
36 changed files with 761 additions and 142 deletions

View File

@@ -165,6 +165,7 @@ static int CheckCertAuth(Port *port);
*/
char *pg_krb_server_keyfile;
bool pg_krb_caseins_users;
bool pg_gss_accept_deleg;
/*----------------------------------------------------------------
@@ -918,6 +919,7 @@ pg_GSS_recvauth(Port *port)
int mtype;
StringInfoData buf;
gss_buffer_desc gbuf;
gss_cred_id_t delegated_creds;
/*
* Use the configured keytab, if there is one. Unfortunately, Heimdal
@@ -947,6 +949,9 @@ pg_GSS_recvauth(Port *port)
*/
port->gss->ctx = GSS_C_NO_CONTEXT;
delegated_creds = GSS_C_NO_CREDENTIAL;
port->gss->delegated_creds = false;
/*
* Loop through GSSAPI message exchange. This exchange can consist of
* multiple messages sent in both directions. First message is always from
@@ -997,7 +1002,7 @@ pg_GSS_recvauth(Port *port)
&port->gss->outbuf,
&gflags,
NULL,
NULL);
pg_gss_accept_deleg ? &delegated_creds : NULL);
/* gbuf no longer used */
pfree(buf.data);
@@ -1009,6 +1014,12 @@ pg_GSS_recvauth(Port *port)
CHECK_FOR_INTERRUPTS();
if (delegated_creds != GSS_C_NO_CREDENTIAL && gflags & GSS_C_DELEG_FLAG)
{
pg_store_delegated_credential(delegated_creds);
port->gss->delegated_creds = true;
}
if (port->gss->outbuf.length != 0)
{
/*

View File

@@ -92,3 +92,56 @@ pg_GSS_error(const char *errmsg,
(errmsg_internal("%s", errmsg),
errdetail_internal("%s: %s", msg_major, msg_minor)));
}
/*
* Store the credentials passed in into the memory cache for later usage.
*
* This allows credentials to be delegated to us for us to use to connect
* to other systems with, using, e.g. postgres_fdw or dblink.
*/
#define GSS_MEMORY_CACHE "MEMORY:"
void
pg_store_delegated_credential(gss_cred_id_t cred)
{
OM_uint32 major,
minor;
gss_OID_set mech;
gss_cred_usage_t usage;
gss_key_value_element_desc cc;
gss_key_value_set_desc ccset;
cc.key = "ccache";
cc.value = GSS_MEMORY_CACHE;
ccset.count = 1;
ccset.elements = &cc;
/* Make the delegated credential only available to current process */
major = gss_store_cred_into(&minor,
cred,
GSS_C_INITIATE, /* credential only used for
* starting libpq connection */
GSS_C_NULL_OID, /* store all */
true, /* overwrite */
true, /* make default */
&ccset,
&mech,
&usage);
if (major != GSS_S_COMPLETE)
{
pg_GSS_error("gss_store_cred", major, minor);
}
/* Credential stored, so we can release our credential handle. */
major = gss_release_cred(&minor, &cred);
if (major != GSS_S_COMPLETE)
{
pg_GSS_error("gss_release_cred", major, minor);
}
/*
* Set KRB5CCNAME for this backend, so that later calls to
* gss_acquire_cred will find the delegated credentials we stored.
*/
setenv("KRB5CCNAME", GSS_MEMORY_CACHE, 1);
}

View File

@@ -497,6 +497,7 @@ secure_open_gssapi(Port *port)
bool complete_next = false;
OM_uint32 major,
minor;
gss_cred_id_t delegated_creds;
/*
* Allocate subsidiary Port data for GSSAPI operations.
@@ -504,6 +505,9 @@ secure_open_gssapi(Port *port)
port->gss = (pg_gssinfo *)
MemoryContextAllocZero(TopMemoryContext, sizeof(pg_gssinfo));
delegated_creds = GSS_C_NO_CREDENTIAL;
port->gss->delegated_creds = false;
/*
* Allocate buffers and initialize state variables. By malloc'ing the
* buffers at this point, we avoid wasting static data space in processes
@@ -588,7 +592,8 @@ secure_open_gssapi(Port *port)
GSS_C_NO_CREDENTIAL, &input,
GSS_C_NO_CHANNEL_BINDINGS,
&port->gss->name, NULL, &output, NULL,
NULL, NULL);
NULL, pg_gss_accept_deleg ? &delegated_creds : NULL);
if (GSS_ERROR(major))
{
pg_GSS_error(_("could not accept GSSAPI security context"),
@@ -605,6 +610,12 @@ secure_open_gssapi(Port *port)
complete_next = true;
}
if (delegated_creds != GSS_C_NO_CREDENTIAL)
{
pg_store_delegated_credential(delegated_creds);
port->gss->delegated_creds = true;
}
/* Done handling the incoming packet, reset our buffer */
PqGSSRecvLength = 0;
@@ -731,3 +742,16 @@ be_gssapi_get_princ(Port *port)
return port->gss->princ;
}
/*
* Return if GSSAPI delegated credentials were included on this
* connection.
*/
bool
be_gssapi_get_deleg(Port *port)
{
if (!port || !port->gss)
return NULL;
return port->gss->delegated_creds;
}