mirror of
https://github.com/postgres/postgres.git
synced 2025-10-24 01:29:19 +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:
@@ -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 */
|
||||
|
||||
@@ -559,7 +559,7 @@ pg_SASL_init(PGconn *conn, int payloadlen)
|
||||
* First, select the password to use for the exchange, complaining if
|
||||
* there isn't one and the selected SASL mechanism needs it.
|
||||
*/
|
||||
if (conn->password_needed)
|
||||
if (conn->password_needed && !conn->scram_client_key_binary)
|
||||
{
|
||||
password = conn->connhost[conn->whichhost].password;
|
||||
if (password == NULL)
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "common/base64.h"
|
||||
#include "common/ip.h"
|
||||
#include "common/link-canary.h"
|
||||
#include "common/scram-common.h"
|
||||
@@ -366,6 +367,12 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
|
||||
"Load-Balance-Hosts", "", 8, /* sizeof("disable") = 8 */
|
||||
offsetof(struct pg_conn, load_balance_hosts)},
|
||||
|
||||
{"scram_client_key", NULL, NULL, NULL, "SCRAM-Client-Key", "D", SCRAM_MAX_KEY_LEN * 2,
|
||||
offsetof(struct pg_conn, scram_client_key)},
|
||||
|
||||
{"scram_server_key", NULL, NULL, NULL, "SCRAM-Server-Key", "D", SCRAM_MAX_KEY_LEN * 2,
|
||||
offsetof(struct pg_conn, scram_server_key)},
|
||||
|
||||
/* Terminating entry --- MUST BE LAST */
|
||||
{NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, 0}
|
||||
@@ -1793,6 +1800,44 @@ pqConnectOptions2(PGconn *conn)
|
||||
else
|
||||
conn->target_server_type = SERVER_TYPE_ANY;
|
||||
|
||||
if (conn->scram_client_key)
|
||||
{
|
||||
int len;
|
||||
|
||||
len = pg_b64_dec_len(strlen(conn->scram_client_key));
|
||||
/* Consider the zero-terminator */
|
||||
if (len != SCRAM_MAX_KEY_LEN + 1)
|
||||
{
|
||||
libpq_append_conn_error(conn, "invalid SCRAM client key length: %d", len);
|
||||
return false;
|
||||
}
|
||||
conn->scram_client_key_len = len;
|
||||
conn->scram_client_key_binary = malloc(len);
|
||||
if (!conn->scram_client_key_binary)
|
||||
goto oom_error;
|
||||
pg_b64_decode(conn->scram_client_key, strlen(conn->scram_client_key),
|
||||
conn->scram_client_key_binary, len);
|
||||
}
|
||||
|
||||
if (conn->scram_server_key)
|
||||
{
|
||||
int len;
|
||||
|
||||
len = pg_b64_dec_len(strlen(conn->scram_server_key));
|
||||
/* Consider the zero-terminator */
|
||||
if (len != SCRAM_MAX_KEY_LEN + 1)
|
||||
{
|
||||
libpq_append_conn_error(conn, "invalid SCRAM server key length: %d", len);
|
||||
return false;
|
||||
}
|
||||
conn->scram_server_key_len = len;
|
||||
conn->scram_server_key_binary = malloc(len);
|
||||
if (!conn->scram_server_key_binary)
|
||||
goto oom_error;
|
||||
pg_b64_decode(conn->scram_server_key, strlen(conn->scram_server_key),
|
||||
conn->scram_server_key_binary, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* validate load_balance_hosts option, and set load_balance_type
|
||||
*/
|
||||
@@ -4704,6 +4749,8 @@ freePGconn(PGconn *conn)
|
||||
free(conn->rowBuf);
|
||||
free(conn->target_session_attrs);
|
||||
free(conn->load_balance_hosts);
|
||||
free(conn->scram_client_key);
|
||||
free(conn->scram_server_key);
|
||||
termPQExpBuffer(&conn->errorMessage);
|
||||
termPQExpBuffer(&conn->workBuffer);
|
||||
|
||||
|
||||
@@ -428,6 +428,8 @@ struct pg_conn
|
||||
char *target_session_attrs; /* desired session properties */
|
||||
char *require_auth; /* name of the expected auth method */
|
||||
char *load_balance_hosts; /* load balance over hosts */
|
||||
char *scram_client_key; /* base64-encoded SCRAM client key */
|
||||
char *scram_server_key; /* base64-encoded SCRAM server key */
|
||||
|
||||
bool cancelRequest; /* true if this connection is used to send a
|
||||
* cancel request, instead of being a normal
|
||||
@@ -518,6 +520,10 @@ struct pg_conn
|
||||
AddrInfo *addr; /* the array of addresses for the currently
|
||||
* tried host */
|
||||
bool send_appname; /* okay to send application_name? */
|
||||
size_t scram_client_key_len;
|
||||
void *scram_client_key_binary; /* binary SCRAM client key */
|
||||
size_t scram_server_key_len;
|
||||
void *scram_server_key_binary; /* binary SCRAM server key */
|
||||
|
||||
/* Miscellaneous stuff */
|
||||
int be_pid; /* PID of backend --- needed for cancels */
|
||||
|
||||
Reference in New Issue
Block a user