1
0
mirror of https://github.com/postgres/postgres.git synced 2025-10-25 13:17:41 +03:00

postgres_fdw: SCRAM authentication pass-through

This enables SCRAM authentication for postgres_fdw when connecting to
a foreign server without having to store a plain-text password on user
mapping options.

This is done by saving the SCRAM ClientKey and ServeryKey from the
client authentication and using those instead of the plain-text
password for the server-side SCRAM exchange.  The new foreign-server
or user-mapping option "use_scram_passthrough" enables this.

Co-authored-by: Matheus Alcantara <mths.dev@pm.me>
Co-authored-by: Peter Eisentraut <peter@eisentraut.org>
Discussion: https://www.postgresql.org/message-id/flat/27b29a35-9b96-46a9-bc1a-914140869dac@gmail.com
This commit is contained in:
Peter Eisentraut
2025-01-15 17:55:18 +01:00
parent b6463ea6ef
commit 761c79508e
14 changed files with 450 additions and 42 deletions

View File

@@ -119,25 +119,28 @@ scram_init(PGconn *conn,
return NULL;
}
/* Normalize the password with SASLprep, if possible */
rc = pg_saslprep(password, &prep_password);
if (rc == SASLPREP_OOM)
if (password)
{
free(state->sasl_mechanism);
free(state);
return NULL;
}
if (rc != SASLPREP_SUCCESS)
{
prep_password = strdup(password);
if (!prep_password)
/* Normalize the password with SASLprep, if possible */
rc = pg_saslprep(password, &prep_password);
if (rc == SASLPREP_OOM)
{
free(state->sasl_mechanism);
free(state);
return NULL;
}
if (rc != SASLPREP_SUCCESS)
{
prep_password = strdup(password);
if (!prep_password)
{
free(state->sasl_mechanism);
free(state);
return NULL;
}
}
state->password = prep_password;
}
state->password = prep_password;
return state;
}
@@ -775,20 +778,31 @@ calculate_client_proof(fe_scram_state *state,
return false;
}
/*
* Calculate SaltedPassword, and store it in 'state' so that we can reuse
* it later in verify_server_signature.
*/
if (scram_SaltedPassword(state->password, state->hash_type,
state->key_length, state->salt, state->saltlen,
state->iterations, state->SaltedPassword,
errstr) < 0 ||
scram_ClientKey(state->SaltedPassword, state->hash_type,
state->key_length, ClientKey, errstr) < 0 ||
scram_H(ClientKey, state->hash_type, state->key_length,
StoredKey, errstr) < 0)
if (state->conn->scram_client_key_binary)
{
memcpy(ClientKey, state->conn->scram_client_key_binary, SCRAM_MAX_KEY_LEN);
}
else
{
/*
* Calculate SaltedPassword, and store it in 'state' so that we can
* reuse it later in verify_server_signature.
*/
if (scram_SaltedPassword(state->password, state->hash_type,
state->key_length, state->salt, state->saltlen,
state->iterations, state->SaltedPassword,
errstr) < 0 ||
scram_ClientKey(state->SaltedPassword, state->hash_type,
state->key_length, ClientKey, errstr) < 0)
{
/* errstr is already filled here */
pg_hmac_free(ctx);
return false;
}
}
if (scram_H(ClientKey, state->hash_type, state->key_length, StoredKey, errstr) < 0)
{
/* errstr is already filled here */
pg_hmac_free(ctx);
return false;
}
@@ -841,12 +855,19 @@ verify_server_signature(fe_scram_state *state, bool *match,
return false;
}
if (scram_ServerKey(state->SaltedPassword, state->hash_type,
state->key_length, ServerKey, errstr) < 0)
if (state->conn->scram_server_key_binary)
{
/* errstr is filled already */
pg_hmac_free(ctx);
return false;
memcpy(ServerKey, state->conn->scram_server_key_binary, SCRAM_MAX_KEY_LEN);
}
else
{
if (scram_ServerKey(state->SaltedPassword, state->hash_type,
state->key_length, ServerKey, errstr) < 0)
{
/* errstr is filled already */
pg_hmac_free(ctx);
return false;
}
}
/* calculate ServerSignature */