1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-02 09:02:37 +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

@ -186,3 +186,4 @@ PQpipelineStatus 183
PQsetTraceFlags 184
PQmblenBounded 185
PQsendFlushRequest 186
PQconnectionUsedGSSAPI 187

View File

@ -58,7 +58,8 @@ pg_GSS_continue(PGconn *conn, int payloadlen)
{
OM_uint32 maj_stat,
min_stat,
lmin_s;
lmin_s,
gss_flags = GSS_C_MUTUAL_FLAG;
gss_buffer_desc ginbuf;
gss_buffer_desc goutbuf;
@ -92,12 +93,19 @@ pg_GSS_continue(PGconn *conn, int payloadlen)
ginbuf.value = NULL;
}
/* Only try to acquire credentials if GSS delegation isn't disabled. */
if (!pg_GSS_have_cred_cache(&conn->gcred))
conn->gcred = GSS_C_NO_CREDENTIAL;
if (conn->gssdeleg && pg_strcasecmp(conn->gssdeleg, "enable") == 0)
gss_flags |= GSS_C_DELEG_FLAG;
maj_stat = gss_init_sec_context(&min_stat,
GSS_C_NO_CREDENTIAL,
conn->gcred,
&conn->gctx,
conn->gtarg_nam,
GSS_C_NO_OID,
GSS_C_MUTUAL_FLAG,
gss_flags,
0,
GSS_C_NO_CHANNEL_BINDINGS,
(ginbuf.value == NULL) ? GSS_C_NO_BUFFER : &ginbuf,
@ -139,6 +147,7 @@ pg_GSS_continue(PGconn *conn, int payloadlen)
{
conn->client_finished_auth = true;
gss_release_name(&lmin_s, &conn->gtarg_nam);
conn->gssapi_used = true;
}
return STATUS_OK;

View File

@ -343,6 +343,10 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
"GSS-library", "", 7, /* sizeof("gssapi") == 7 */
offsetof(struct pg_conn, gsslib)},
{"gssdeleg", "PGGSSDELEG", NULL, NULL,
"GSS-delegation", "", 8, /* sizeof("disable") == 8 */
offsetof(struct pg_conn, gssdeleg)},
{"replication", NULL, NULL, NULL,
"Replication", "D", 5,
offsetof(struct pg_conn, replication)},
@ -617,6 +621,7 @@ pqDropServerData(PGconn *conn)
conn->auth_req_received = false;
conn->client_finished_auth = false;
conn->password_needed = false;
conn->gssapi_used = false;
conn->write_failed = false;
free(conn->write_err_msg);
conn->write_err_msg = NULL;
@ -4448,6 +4453,7 @@ freePGconn(PGconn *conn)
free(conn->gssencmode);
free(conn->krbsrvname);
free(conn->gsslib);
free(conn->gssdeleg);
free(conn->connip);
/* Note that conn->Pfdebug is not ours to close or free */
free(conn->write_err_msg);
@ -7312,6 +7318,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)
{

View File

@ -477,7 +477,8 @@ pqsecure_open_gss(PGconn *conn)
{
ssize_t ret;
OM_uint32 major,
minor;
minor,
gss_flags = GSS_REQUIRED_FLAGS;
uint32 netlen;
PostgresPollingStatusType result;
gss_buffer_desc input = GSS_C_EMPTY_BUFFER,
@ -621,13 +622,30 @@ pqsecure_open_gss(PGconn *conn)
if (ret != STATUS_OK)
return PGRES_POLLING_FAILED;
if (conn->gssdeleg && pg_strcasecmp(conn->gssdeleg, "enable") == 0)
{
/* Acquire credentials if possbile */
if (conn->gcred == GSS_C_NO_CREDENTIAL)
(void) pg_GSS_have_cred_cache(&conn->gcred);
/*
* We have credentials and gssdeleg is enabled, so request credential
* delegation. This may or may not actually result in credentials
* being delegated- it depends on if the forwardable flag has been set
* in the credential and if the server is configured to accept
* delegated credentials.
*/
if (conn->gcred != GSS_C_NO_CREDENTIAL)
gss_flags |= GSS_C_DELEG_FLAG;
}
/*
* Call GSS init context, either with an empty input, or with a complete
* packet from the server.
*/
major = gss_init_sec_context(&minor, conn->gcred, &conn->gctx,
conn->gtarg_nam, GSS_C_NO_OID,
GSS_REQUIRED_FLAGS, 0, 0, &input, NULL,
gss_flags, 0, 0, &input, NULL,
&output, NULL, NULL);
/* GSS Init Sec Context uses the whole packet, so clear it */
@ -647,6 +665,7 @@ pqsecure_open_gss(PGconn *conn)
* to do GSS wrapping/unwrapping.
*/
conn->gssenc = true;
conn->gssapi_used = true;
/* Clean up */
gss_release_cred(&minor, &conn->gcred);

View File

@ -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);

View File

@ -404,6 +404,7 @@ struct pg_conn
char *krbsrvname; /* Kerberos service name */
char *gsslib; /* What GSS library to use ("gssapi" or
* "sspi") */
char *gssdeleg; /* Try to delegate GSS credentials? */
char *ssl_min_protocol_version; /* minimum TLS protocol version */
char *ssl_max_protocol_version; /* maximum TLS protocol version */
char *target_session_attrs; /* desired session properties */
@ -465,6 +466,7 @@ struct pg_conn
int sversion; /* server version, e.g. 70401 for 7.4.1 */
bool auth_req_received; /* true if any type of auth req received */
bool password_needed; /* true if server demanded a password */
bool gssapi_used; /* true if authenticated via gssapi */
bool sigpipe_so; /* have we masked SIGPIPE via SO_NOSIGPIPE? */
bool sigpipe_flag; /* can we mask SIGPIPE via MSG_NOSIGNAL? */
bool write_failed; /* have we had a write failure on sock? */