Thanks for the comments!

On 04/12/24 19:11, Jacob Champion wrote:
This is achieved by storing the SCRAM ClientKey and ServerKey obtained
during client authentication with the backend. These keys are then
used to complete the SCRAM exchange between the backend and the fdw
server, eliminating the need to derive them from a stored plain-text
password.
What are the assumptions that have to be made for pass-through SCRAM
to succeed? Is it just "the two servers have identical verifiers for
the user," or are there others?
Yes, from the tests that I've performed I would say that this is the only requirement.

It looks like the patch is using the following property [1]:

    If an attacker obtains the authentication information from the
    authentication repository and either eavesdrops on one authentication
    exchange or impersonates a server, the attacker gains the ability to
    impersonate that user to all servers providing SCRAM access using the
    same hash function, password, iteration count, and salt.  For this
    reason, it is important to use randomly generated salt values.

It makes me a little uneasy to give users a reason to copy identical
salts/verifiers around... But for e.g. a loopback connection, it seems
like there'd be no additional risk. Is that the target use case?
I think that both can be use cases. In case of using with different servers it can be another option over the plain-text password approach.

I'm attaching a v2 patch with a TAP test that validate the both use cases. For connections with different servers an ALTER ROLE <role> PASSWORD <encrypted_password> is required, so that both servers have identical verifiers.


-    pg_hmac_ctx *ctx = pg_hmac_create(state->hash_type);
+    pg_hmac_ctx *ctx = pg_hmac_create(PG_SHA256);

Why was that change made?

Not needed, sorry. Fixed on v2

--
Matheus Alcantara
EDB: https://www.enterprisedb.com
From 69f41167ab3cf42f5a262403145ae7b53c77a38c Mon Sep 17 00:00:00 2001
From: Matheus Alcantara <mths....@pm.me>
Date: Tue, 19 Nov 2024 15:37:57 -0300
Subject: [PATCH v2 1/2] postgres_fdw: SCRAM authentication pass-through

This commit enable SCRAM authentication for postgres_fdw when connecting
to a fdw 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.
---
 contrib/postgres_fdw/Makefile            |   1 +
 contrib/postgres_fdw/connection.c        |  67 ++++++++++-
 contrib/postgres_fdw/meson.build         |   5 +
 contrib/postgres_fdw/option.c            |   3 +
 contrib/postgres_fdw/t/001_auth_scram.pl | 137 +++++++++++++++++++++++
 src/backend/libpq/auth-scram.c           |  14 ++-
 src/include/libpq/libpq-be.h             |   9 ++
 src/interfaces/libpq/fe-auth-scram.c     |  29 ++++-
 src/interfaces/libpq/fe-auth.c           |   2 +-
 src/interfaces/libpq/fe-connect.c        |  31 +++++
 src/interfaces/libpq/libpq-int.h         |   6 +
 11 files changed, 291 insertions(+), 13 deletions(-)
 create mode 100644 contrib/postgres_fdw/t/001_auth_scram.pl

diff --git a/contrib/postgres_fdw/Makefile b/contrib/postgres_fdw/Makefile
index 88fdce40d6..6c12c8e925 100644
--- a/contrib/postgres_fdw/Makefile
+++ b/contrib/postgres_fdw/Makefile
@@ -8,6 +8,7 @@ OBJS = \
        option.o \
        postgres_fdw.o \
        shippable.o
+TAP_TESTS = 1
 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 2326f391d3..e0e1ebe0d4 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -19,6 +19,7 @@
 #include "access/xact.h"
 #include "catalog/pg_user_mapping.h"
 #include "commands/defrem.h"
+#include "common/base64.h"
 #include "funcapi.h"
 #include "libpq/libpq-be.h"
 #include "libpq/libpq-be-fe-helpers.h"
@@ -168,6 +169,7 @@ static void pgfdw_finish_abort_cleanup(List 
*pending_entries,
 static void pgfdw_security_check(const char **keywords, const char **values,
                                                                 UserMapping 
*user, PGconn *conn);
 static bool UserMappingPasswordRequired(UserMapping *user);
+static bool UseScramPassthrough(ForeignServer *server, UserMapping *user);
 static bool disconnect_cached_connections(Oid serverid);
 static void postgres_fdw_get_connections_internal(FunctionCallInfo fcinfo,
                                                                                
                  enum pgfdwVersion api_version);
@@ -476,7 +478,7 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
                 * for application_name, fallback_application_name, 
client_encoding,
                 * end marker.
                 */
-               n = list_length(server->options) + list_length(user->options) + 
4;
+               n = list_length(server->options) + list_length(user->options) + 
4 + 2;
                keywords = (const char **) palloc(n * sizeof(char *));
                values = (const char **) palloc(n * sizeof(char *));
 
@@ -545,10 +547,37 @@ connect_pg_server(ForeignServer *server, UserMapping 
*user)
                values[n] = GetDatabaseEncodingName();
                n++;
 
+               if (MyProcPort->has_scram_keys && UseScramPassthrough(server, 
user))
+               {
+                       int                     len;
+
+                       keywords[n] = "scram_client_key";
+                       len = 
pg_b64_enc_len(sizeof(MyProcPort->scram_ClientKey));
+                       /* don't forget the zero-terminator */
+                       values[n] = palloc0(len+1);
+                       pg_b64_encode((const char *) 
MyProcPort->scram_ClientKey,
+                                                 
sizeof(MyProcPort->scram_ClientKey),
+                                                 (char *) values[n], len);
+                       n++;
+
+                       keywords[n] = "scram_server_key";
+                       len = 
pg_b64_enc_len(sizeof(MyProcPort->scram_ServerKey));
+                       /* don't forget the zero-terminator */
+                       values[n] = palloc0(len+1);
+                       pg_b64_encode((const char *) 
MyProcPort->scram_ServerKey,
+                                                 
sizeof(MyProcPort->scram_ServerKey),
+                                                 (char *) values[n], len);
+                       n++;
+               }
+
                keywords[n] = values[n] = NULL;
 
-               /* verify the set of connection parameters */
-               check_conn_params(keywords, values, user);
+               /*
+                * Verify the set of connection parameters only if scram 
pass-through
+                * is not being used because the password is not necessary.
+                */
+               if (!(MyProcPort->has_scram_keys && UseScramPassthrough(server, 
user)))
+                       check_conn_params(keywords, values, user);
 
                /* first time, allocate or get the custom wait event */
                if (pgfdw_we_connect == 0)
@@ -566,8 +595,12 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
                                                        server->servername),
                                         errdetail_internal("%s", 
pchomp(PQerrorMessage(conn)))));
 
-               /* Perform post-connection security checks */
-               pgfdw_security_check(keywords, values, user, conn);
+               /*
+                * Perform post-connection security checks only if scram 
pass-through
+                * is not being used because the password is not necessary.
+                */
+               if (!(MyProcPort->has_scram_keys && UseScramPassthrough(server, 
user)))
+                       pgfdw_security_check(keywords, values, user, conn);
 
                /* Prepare new session for use */
                configure_remote_session(conn);
@@ -620,6 +653,30 @@ UserMappingPasswordRequired(UserMapping *user)
        return true;
 }
 
+static bool
+UseScramPassthrough(ForeignServer *server, UserMapping *user)
+{
+       ListCell   *cell;
+
+       foreach(cell, server->options)
+       {
+               DefElem    *def = (DefElem *) lfirst(cell);
+
+               if (strcmp(def->defname, "use_scram_passthrough") == 0)
+                       return defGetBoolean(def);
+       }
+
+       foreach(cell, user->options)
+       {
+               DefElem    *def = (DefElem *) lfirst(cell);
+
+               if (strcmp(def->defname, "use_scram_passthrough") == 0)
+                       return defGetBoolean(def);
+       }
+
+       return false;
+}
+
 /*
  * For non-superusers, insist that the connstr specify a password or that the
  * user provided their own GSSAPI delegated credentials.  This
diff --git a/contrib/postgres_fdw/meson.build b/contrib/postgres_fdw/meson.build
index 3014086ba6..27d07188fc 100644
--- a/contrib/postgres_fdw/meson.build
+++ b/contrib/postgres_fdw/meson.build
@@ -41,4 +41,9 @@ tests += {
     ],
     'regress_args': ['--dlpath', meson.build_root() / 'src/test/regress'],
   },
+  'tap': {
+      'tests': [
+        't/001_auth_scram.pl',
+      ],
+  },
 }
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index 232d85354b..15abc64381 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -279,6 +279,9 @@ InitPgFdwOptions(void)
                {"analyze_sampling", ForeignServerRelationId, false},
                {"analyze_sampling", ForeignTableRelationId, false},
 
+               {"use_scram_passthrough", ForeignServerRelationId, false},
+               {"use_scram_passthrough", UserMappingRelationId, false},
+
                /*
                 * sslcert and sslkey are in fact libpq options, but we repeat 
them
                 * here to allow them to appear in both foreign server context 
(when
diff --git a/contrib/postgres_fdw/t/001_auth_scram.pl 
b/contrib/postgres_fdw/t/001_auth_scram.pl
new file mode 100644
index 0000000000..8a6d6a05c6
--- /dev/null
+++ b/contrib/postgres_fdw/t/001_auth_scram.pl
@@ -0,0 +1,137 @@
+# Copyright (c) 2021-2024, PostgreSQL Global Development Group
+
+# Test SCRAM authentication when opening a new connection with a foreign
+# server.
+#
+# The test is executed by testing the SCRAM authentifcation on a looplback
+# connection on the same server and with different servers.
+
+use strict;
+use warnings FATAL => 'all';
+use PostgreSQL::Test::Utils;
+use PostgreSQL::Test::Cluster;
+use Test::More;
+
+my $hostaddr = '127.0.0.1';
+my $user = "user01";
+
+my $db0 = "db0"; # For node1
+my $db1 = "db1"; # For node1
+my $db2 = "db2"; # For node2
+my $fdw_server = "db1_fdw";
+my $fdw_server2 = "db2_fdw";
+
+my $node1 = PostgreSQL::Test::Cluster->new('node1');
+my $node2 = PostgreSQL::Test::Cluster->new('node2');
+
+$node1->init;
+$node2->init;
+
+$node1->start;
+$node2->start;
+
+# Test setup
+
+$node1->safe_psql('postgres', qq'CREATE USER $user WITH password \'pass\'');
+$node2->safe_psql('postgres', qq'CREATE USER $user WITH password \'pass\'');
+$ENV{PGPASSWORD} = "pass";
+
+$node1->safe_psql('postgres', qq'CREATE DATABASE $db0');
+$node1->safe_psql('postgres', qq'CREATE DATABASE $db1');
+$node2->safe_psql('postgres', qq'CREATE DATABASE $db2');
+
+setup_table($node1, $db1, "t");
+setup_table($node2, $db2, "t2");
+
+$node1->safe_psql($db0, 'CREATE EXTENSION IF NOT EXISTS postgres_fdw');
+setup_fdw_server($node1, $db0, $fdw_server, $node1, $db1);
+setup_fdw_server($node1, $db0, $fdw_server2, $node2, $db2);
+
+setup_user_mapping($node1, $db0, $fdw_server);
+setup_user_mapping($node1, $db0, $fdw_server2);
+
+# Make the user have the same SCRAM key on both servers. Forcing to have the
+# same iteration and salt.
+my $rolpassword = $node1->safe_psql('postgres', qq"SELECT rolpassword FROM 
pg_authid WHERE rolname = '$user';");
+$node2->safe_psql('postgres', qq"ALTER ROLE $user PASSWORD '$rolpassword'");
+
+setup_pghba($node1);
+setup_pghba($node2);
+
+# End of test setup
+
+test_fdw_auth($node1, $db0, "t", $fdw_server, "SCRAM auth on the same database 
cluster must succeed");
+test_fdw_auth($node1, $db0, "t2", $fdw_server2, "SCRAM auth on a different 
database cluster must succeed");
+test_auth($node2, $db2, "t2",  "SCRAM auth directly on foreign server should 
still succeed");
+
+# Helper functions
+
+sub test_auth
+{
+       local $Test::Builder::Level = $Test::Builder::Level + 1;
+
+       my ($node, $db, $tbl, $testname) = @_;
+       my $connstr = $node->connstr($db) . qq' user=$user';
+
+       my $ret = $node->safe_psql($db, qq'SELECT count(1) FROM $tbl',
+               connstr=>$connstr);
+
+       is($ret, '10', $testname);
+}
+
+sub test_fdw_auth
+{
+       local $Test::Builder::Level = $Test::Builder::Level + 1;
+
+       my ($node, $db, $tbl, $fdw, $testname) = @_;
+       my $connstr = $node->connstr($db) . qq' user=$user';
+
+       $node->safe_psql($db, qq'IMPORT FOREIGN SCHEMA public LIMIT TO($tbl) 
FROM SERVER $fdw INTO public;',
+               connstr=>$connstr);
+
+       test_auth($node, $db, $tbl, $testname);
+}
+
+sub setup_pghba
+{
+       my ($node) = @_;
+
+       unlink($node->data_dir . '/pg_hba.conf');
+       $node->append_conf(
+               'pg_hba.conf', qq{
+       local   all             all                                     
scram-sha-256
+       host    all             all             $hostaddr/32            
scram-sha-256
+       });
+
+       $node->restart;
+}
+
+sub setup_user_mapping
+{
+       my ($node, $db, $fdw) = @_;
+
+       $node->safe_psql($db, qq'CREATE USER MAPPING FOR $user SERVER $fdw 
OPTIONS (user \'$user\');');
+       $node->safe_psql($db, qq'GRANT USAGE ON FOREIGN SERVER $fdw to $user;');
+       $node->safe_psql($db, qq'GRANT ALL ON SCHEMA public to $user');
+}
+
+sub setup_fdw_server
+{
+       my ($node, $db, $fdw, $fdw_node, $dbname) = @_;
+       my $host = $fdw_node->host;
+       my $port = $fdw_node->port;
+
+       $node->safe_psql($db, qq'CREATE SERVER $fdw FOREIGN DATA WRAPPER 
postgres_fdw options (
+               host \'$host\', port \'$port\', dbname \'$dbname\', 
use_scram_passthrough \'true\') ');
+}
+
+sub setup_table
+{
+       my ($node, $db, $tbl) = @_;
+
+       $node->safe_psql($db, qq'CREATE TABLE $tbl AS SELECT g,g+1 FROM 
generate_series(1,10) g(g)');
+       $node->safe_psql($db, qq'GRANT USAGE ON SCHEMA public to $user');
+       $node->safe_psql($db, qq'GRANT SELECT ON $tbl to $user');
+}
+
+done_testing();
diff --git a/src/backend/libpq/auth-scram.c b/src/backend/libpq/auth-scram.c
index 8c5b6d9c67..7711ed63e0 100644
--- a/src/backend/libpq/auth-scram.c
+++ b/src/backend/libpq/auth-scram.c
@@ -101,6 +101,7 @@
 #include "libpq/crypt.h"
 #include "libpq/sasl.h"
 #include "libpq/scram.h"
+#include "miscadmin.h"
 
 static void scram_get_mechanisms(Port *port, StringInfo buf);
 static void *scram_init(Port *port, const char *selected_mech,
@@ -144,6 +145,7 @@ typedef struct
 
        int                     iterations;
        char       *salt;                       /* base64-encoded */
+       uint8           ClientKey[SCRAM_MAX_KEY_LEN];
        uint8           StoredKey[SCRAM_MAX_KEY_LEN];
        uint8           ServerKey[SCRAM_MAX_KEY_LEN];
 
@@ -462,6 +464,13 @@ scram_exchange(void *opaq, const char *input, int inputlen,
        if (*output)
                *outputlen = strlen(*output);
 
+       if (result == PG_SASL_EXCHANGE_SUCCESS && state->state == 
SCRAM_AUTH_FINISHED)
+       {
+               memcpy(MyProcPort->scram_ClientKey, state->ClientKey, 
sizeof(MyProcPort->scram_ClientKey));
+               memcpy(MyProcPort->scram_ServerKey, state->ServerKey, 
sizeof(MyProcPort->scram_ServerKey));
+               MyProcPort->has_scram_keys = true;
+       }
+
        return result;
 }
 
@@ -1140,7 +1149,6 @@ static bool
 verify_client_proof(scram_state *state)
 {
        uint8           ClientSignature[SCRAM_MAX_KEY_LEN];
-       uint8           ClientKey[SCRAM_MAX_KEY_LEN];
        uint8           client_StoredKey[SCRAM_MAX_KEY_LEN];
        pg_hmac_ctx *ctx = pg_hmac_create(state->hash_type);
        int                     i;
@@ -1173,10 +1181,10 @@ verify_client_proof(scram_state *state)
 
        /* Extract the ClientKey that the client calculated from the proof */
        for (i = 0; i < state->key_length; i++)
-               ClientKey[i] = state->ClientProof[i] ^ ClientSignature[i];
+               state->ClientKey[i] = state->ClientProof[i] ^ 
ClientSignature[i];
 
        /* Hash it one more time, and compare with StoredKey */
-       if (scram_H(ClientKey, state->hash_type, state->key_length,
+       if (scram_H(state->ClientKey, state->hash_type, state->key_length,
                                client_StoredKey, &errstr) < 0)
                elog(ERROR, "could not hash stored key: %s", errstr);
 
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 9109b2c334..4eb9e80523 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -18,6 +18,8 @@
 #ifndef LIBPQ_BE_H
 #define LIBPQ_BE_H
 
+#include "common/scram-common.h"
+
 #include <sys/time.h>
 #ifdef USE_OPENSSL
 #include <openssl/ssl.h>
@@ -181,6 +183,13 @@ typedef struct Port
        int                     keepalives_count;
        int                     tcp_user_timeout;
 
+       /*
+        * SCRAM structures.
+        */
+       uint8           scram_ClientKey[SCRAM_MAX_KEY_LEN];
+       uint8           scram_ServerKey[SCRAM_MAX_KEY_LEN];
+       bool            has_scram_keys; /* true if the above two are valid */
+
        /*
         * GSSAPI structures.
         */
diff --git a/src/interfaces/libpq/fe-auth-scram.c 
b/src/interfaces/libpq/fe-auth-scram.c
index 0bb820e0d9..7beb5a9d31 100644
--- a/src/interfaces/libpq/fe-auth-scram.c
+++ b/src/interfaces/libpq/fe-auth-scram.c
@@ -119,6 +119,8 @@ scram_init(PGconn *conn,
                return NULL;
        }
 
+       if (password)
+       {
        /* Normalize the password with SASLprep, if possible */
        rc = pg_saslprep(password, &prep_password);
        if (rc == SASLPREP_OOM)
@@ -138,6 +140,7 @@ scram_init(PGconn *conn,
                }
        }
        state->password = prep_password;
+       }
 
        return state;
 }
@@ -775,6 +778,12 @@ calculate_client_proof(fe_scram_state *state,
                return false;
        }
 
+       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.
@@ -783,15 +792,20 @@ calculate_client_proof(fe_scram_state *state,
                                                         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)
+                       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)
+       {
+               pg_hmac_free(ctx);
+               return false;
+       }
 
        if (pg_hmac_init(ctx, StoredKey, state->key_length) < 0 ||
                pg_hmac_update(ctx,
@@ -841,6 +855,12 @@ verify_server_signature(fe_scram_state *state, bool *match,
                return false;
        }
 
+       if (state->conn->scram_server_key_binary)
+       {
+               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)
        {
@@ -848,6 +868,7 @@ verify_server_signature(fe_scram_state *state, bool *match,
                pg_hmac_free(ctx);
                return false;
        }
+       }
 
        /* calculate ServerSignature */
        if (pg_hmac_init(ctx, ServerKey, state->key_length) < 0 ||
diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c
index 20d3427e94..ef1c965cd5 100644
--- a/src/interfaces/libpq/fe-auth.c
+++ b/src/interfaces/libpq/fe-auth.c
@@ -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)
diff --git a/src/interfaces/libpq/fe-connect.c 
b/src/interfaces/libpq/fe-connect.c
index aaf87e8e88..464cefd901 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -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"
@@ -365,6 +366,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}
@@ -1792,6 +1799,28 @@ 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));
+               conn->scram_client_key_len = len;
+               conn->scram_client_key_binary = malloc(len);
+               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));
+               conn->scram_server_key_len = len;
+               conn->scram_server_key_binary = malloc(len);
+               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
         */
@@ -4703,6 +4732,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);
 
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 08cc391cbd..17b81c81f4 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -427,6 +427,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;
+       char       *scram_server_key;
 
        bool            cancelRequest;  /* true if this connection is used to 
send a
                                                                 * cancel 
request, instead of being a normal
@@ -517,6 +519,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;
+       char       *scram_client_key_binary;
+       size_t          scram_server_key_len;
+       char       *scram_server_key_binary;
 
        /* Miscellaneous stuff */
        int                     be_pid;                 /* PID of backend --- 
needed for cancels */
-- 
2.39.3 (Apple Git-146)

Reply via email to