On Fri, Dec 22, 2017 at 11:59 AM, Michael Paquier
<michael.paqu...@gmail.com> wrote:
> I have looked at how things could be done in symmetry for both the frontend
> and backend code, and I have produced the attached patch 0002, which
> can be applied on top of 0001 implementing tls-server-end-point. This
> simplifies the interfaces to initialize the SCRAM status data by saving
> into scram_state and fe_scram_state respectively Port* and PGconn* which
> holds most of the data needed for the exchange. With this patch, cbind_data
> is generated only if a specific channel binding type is used with the
> appropriate data. So if no channel binding is used there is no additional
> SSL call done to get the TLS finished data or the server certificate hash.
>
> 0001 has no real changes compared to the last versions.

Second thoughts on 0002 as there is actually no need to move around
errorMessage if the PGconn* pointer is saved in the SCRAM status data
as both are linked. The attached simplifies the logic even more.
-- 
Michael
From 69dcf31f5ce5938f9f56a94bf55c8439ea53ed27 Mon Sep 17 00:00:00 2001
From: Michael Paquier <mich...@paquier.xyz>
Date: Fri, 22 Dec 2017 10:49:10 +0900
Subject: [PATCH 1/2] Implement channel binding tls-server-end-point for SCRAM

As referenced in RFC 5929, this channel binding is not the default value
and uses a hash of the certificate as binding data. On the frontend, this
can be resumed in getting the data from SSL_get_peer_certificate() and
on the backend SSL_get_certificate().

The hashing algorithm needs also to switch to SHA-256 if the signature
algorithm is MD5 or SHA-1, so let's be careful about that.
---
 doc/src/sgml/protocol.sgml               |  5 +-
 src/backend/libpq/auth-scram.c           | 26 ++++++++---
 src/backend/libpq/auth.c                 |  8 +++-
 src/backend/libpq/be-secure-openssl.c    | 61 +++++++++++++++++++++++++
 src/include/common/scram-common.h        |  1 +
 src/include/libpq/libpq-be.h             |  1 +
 src/include/libpq/scram.h                |  3 +-
 src/interfaces/libpq/fe-auth-scram.c     | 18 ++++++--
 src/interfaces/libpq/fe-auth.c           | 12 ++++-
 src/interfaces/libpq/fe-auth.h           |  4 +-
 src/interfaces/libpq/fe-secure-openssl.c | 78 ++++++++++++++++++++++++++++++++
 src/interfaces/libpq/libpq-int.h         |  1 +
 src/test/ssl/t/002_scram.pl              |  5 +-
 13 files changed, 207 insertions(+), 16 deletions(-)

diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml
index 8174e3defa..365f72b51d 100644
--- a/doc/src/sgml/protocol.sgml
+++ b/doc/src/sgml/protocol.sgml
@@ -1576,8 +1576,9 @@ the password is in.
   <para>
 <firstterm>Channel binding</firstterm> is supported in PostgreSQL builds with
 SSL support. The SASL mechanism name for SCRAM with channel binding
-is <literal>SCRAM-SHA-256-PLUS</literal>.  The only channel binding type
-supported at the moment is <literal>tls-unique</literal>, defined in RFC 5929.
+is <literal>SCRAM-SHA-256-PLUS</literal>.  Two channel binding types are
+supported at the moment: <literal>tls-unique</literal>, which is the default,
+and <literal>tls-server-end-point</literal>, both defined in RFC 5929.
   </para>
 
 <procedure>
diff --git a/src/backend/libpq/auth-scram.c b/src/backend/libpq/auth-scram.c
index d52a763457..849587d141 100644
--- a/src/backend/libpq/auth-scram.c
+++ b/src/backend/libpq/auth-scram.c
@@ -114,6 +114,8 @@ typedef struct
 	bool		ssl_in_use;
 	const char *tls_finished_message;
 	size_t		tls_finished_len;
+	const char *certificate_hash;
+	size_t		certificate_hash_len;
 	char	   *channel_binding_type;
 
 	int			iterations;
@@ -176,7 +178,9 @@ pg_be_scram_init(const char *username,
 				 const char *shadow_pass,
 				 bool ssl_in_use,
 				 const char *tls_finished_message,
-				 size_t tls_finished_len)
+				 size_t tls_finished_len,
+				 const char *certificate_hash,
+				 size_t certificate_hash_len)
 {
 	scram_state *state;
 	bool		got_verifier;
@@ -187,6 +191,8 @@ pg_be_scram_init(const char *username,
 	state->ssl_in_use = ssl_in_use;
 	state->tls_finished_message = tls_finished_message;
 	state->tls_finished_len = tls_finished_len;
+	state->certificate_hash = certificate_hash;
+	state->certificate_hash_len = certificate_hash_len;
 	state->channel_binding_type = NULL;
 
 	/*
@@ -857,13 +863,15 @@ read_client_first_message(scram_state *state, char *input)
 				}
 
 				/*
-				 * Read value provided by client; only tls-unique is supported
-				 * for now.  (It is not safe to print the name of an
-				 * unsupported binding type in the error message.  Pranksters
-				 * could print arbitrary strings into the log that way.)
+				 * Read value provided by client; only tls-unique and
+				 * tls-server-end-point are supported for now.  (It is
+				 * not safe to print the name of an unsupported binding
+				 * type in the error message.  Pranksters could print
+				 * arbitrary strings into the log that way.)
 				 */
 				channel_binding_type = read_attr_value(&input, 'p');
-				if (strcmp(channel_binding_type, SCRAM_CHANNEL_BINDING_TLS_UNIQUE) != 0)
+				if (strcmp(channel_binding_type, SCRAM_CHANNEL_BINDING_TLS_UNIQUE) != 0 &&
+					strcmp(channel_binding_type, SCRAM_CHANNEL_BINDING_TLS_ENDPOINT) != 0)
 					ereport(ERROR,
 							(errcode(ERRCODE_PROTOCOL_VIOLATION),
 							 (errmsg("unsupported SCRAM channel-binding type"))));
@@ -1123,6 +1131,12 @@ read_client_final_message(scram_state *state, char *input)
 			cbind_data = state->tls_finished_message;
 			cbind_data_len = state->tls_finished_len;
 		}
+		else if (strcmp(state->channel_binding_type,
+						SCRAM_CHANNEL_BINDING_TLS_ENDPOINT) == 0)
+		{
+			cbind_data = state->certificate_hash;
+			cbind_data_len = state->certificate_hash_len;
+		}
 		else
 		{
 			/* should not happen */
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index b7f9bb1669..700a3bffa4 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -875,6 +875,8 @@ CheckSCRAMAuth(Port *port, char *shadow_pass, char **logdetail)
 	bool		initial;
 	char	   *tls_finished = NULL;
 	size_t		tls_finished_len = 0;
+	char	   *certificate_hash = NULL;
+	size_t		certificate_hash_len = 0;
 
 	/*
 	 * SASL auth is not supported for protocol versions before 3, because it
@@ -923,6 +925,8 @@ CheckSCRAMAuth(Port *port, char *shadow_pass, char **logdetail)
 	if (port->ssl_in_use)
 	{
 		tls_finished = be_tls_get_peer_finished(port, &tls_finished_len);
+		certificate_hash = be_tls_get_certificate_hash(port,
+													   &certificate_hash_len);
 	}
 #endif
 
@@ -941,7 +945,9 @@ CheckSCRAMAuth(Port *port, char *shadow_pass, char **logdetail)
 								  shadow_pass,
 								  port->ssl_in_use,
 								  tls_finished,
-								  tls_finished_len);
+								  tls_finished_len,
+								  certificate_hash,
+								  certificate_hash_len);
 
 	/*
 	 * Loop through SASL message exchange.  This exchange can consist of
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 1e3e19f5e0..e3e8a535c8 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -1239,6 +1239,67 @@ be_tls_get_peer_finished(Port *port, size_t *len)
 	return result;
 }
 
+/*
+ * Get the server certificate hash for authentication purposes. Per
+ * RFC 5929 and tls-server-end-point, the TLS server's certificate bytes
+ * need to be hashed with SHA-256 if its signature algorithm is MD5 or
+ * SHA-1 as per RFC 5929 (https://tools.ietf.org/html/rfc5929#section-4.1).
+ * If something else is used, the same hash as the signature algorithm is
+ * used. The result is a palloc'd hash of the server certificate with its
+ * size, and NULL if there is no certificate available.
+ */
+char *
+be_tls_get_certificate_hash(Port *port, size_t *len)
+{
+	char	*cert_hash = NULL;
+	X509	*server_cert;
+
+	*len = 0;
+	server_cert = SSL_get_certificate(port->ssl);
+
+	if (server_cert != NULL)
+	{
+		const EVP_MD   *algo_type = NULL;
+		char			hash[EVP_MAX_MD_SIZE];	/* size for SHA-512 */
+		unsigned int	hash_size;
+		int				algo_nid;
+
+		/*
+		 * Get the signature algorithm of the certificate to determine the
+		 * hash algorithm to use for the result.
+		 */
+		if (!OBJ_find_sigid_algs(X509_get_signature_nid(server_cert),
+								 &algo_nid, NULL))
+			elog(ERROR, "could not find signature algorithm");
+
+		switch (algo_nid)
+		{
+			case NID_md5:
+			case NID_sha1:
+				algo_type = EVP_sha256();
+				break;
+
+			default:
+				algo_type = EVP_get_digestbynid(algo_nid);
+				if (algo_type == NULL)
+					elog(ERROR, "could not find digest for NID %s",
+						 OBJ_nid2sn(algo_nid));
+				break;
+		}
+
+		/* generate and save the certificate hash */
+		if (!X509_digest(server_cert, algo_type, (unsigned char *) hash,
+						 &hash_size))
+			elog(ERROR, "could not generate server certificate hash");
+
+		cert_hash = (char *) palloc(hash_size);
+		memcpy(cert_hash, hash, hash_size);
+		*len = hash_size;
+	}
+
+	return cert_hash;
+}
+
 /*
  * Convert an X509 subject name to a cstring.
  *
diff --git a/src/include/common/scram-common.h b/src/include/common/scram-common.h
index 857a60e71f..5aec5cadb8 100644
--- a/src/include/common/scram-common.h
+++ b/src/include/common/scram-common.h
@@ -21,6 +21,7 @@
 
 /* Channel binding types */
 #define SCRAM_CHANNEL_BINDING_TLS_UNIQUE    "tls-unique"
+#define SCRAM_CHANNEL_BINDING_TLS_ENDPOINT	"tls-server-end-point"
 
 /* Length of SCRAM keys (client and server) */
 #define SCRAM_KEY_LEN				PG_SHA256_DIGEST_LENGTH
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 856e0439d5..cf9d8b7870 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -210,6 +210,7 @@ extern void be_tls_get_version(Port *port, char *ptr, size_t len);
 extern void be_tls_get_cipher(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peerdn_name(Port *port, char *ptr, size_t len);
 extern char *be_tls_get_peer_finished(Port *port, size_t *len);
+extern char *be_tls_get_certificate_hash(Port *port, size_t *len);
 #endif
 
 extern ProtocolVersion FrontendProtocol;
diff --git a/src/include/libpq/scram.h b/src/include/libpq/scram.h
index 2c245813d6..7c8f009a3b 100644
--- a/src/include/libpq/scram.h
+++ b/src/include/libpq/scram.h
@@ -21,7 +21,8 @@
 /* Routines dedicated to authentication */
 extern void *pg_be_scram_init(const char *username, const char *shadow_pass,
 				 bool ssl_in_use, const char *tls_finished_message,
-				 size_t tls_finished_len);
+				 size_t tls_finished_len, const char *certificate_hash,
+				 size_t certificate_hash_len);
 extern int pg_be_scram_exchange(void *opaq, char *input, int inputlen,
 					 char **output, int *outputlen, char **logdetail);
 
diff --git a/src/interfaces/libpq/fe-auth-scram.c b/src/interfaces/libpq/fe-auth-scram.c
index b8f7a6b5be..a56fccf12e 100644
--- a/src/interfaces/libpq/fe-auth-scram.c
+++ b/src/interfaces/libpq/fe-auth-scram.c
@@ -47,6 +47,8 @@ typedef struct
 	bool		ssl_in_use;
 	char	   *tls_finished_message;
 	size_t		tls_finished_len;
+	char	   *certificate_hash;
+	size_t		certificate_hash_len;
 	char	   *sasl_mechanism;
 	const char *channel_binding_type;
 
@@ -95,7 +97,9 @@ pg_fe_scram_init(const char *username,
 				 const char *sasl_mechanism,
 				 const char *channel_binding_type,
 				 char *tls_finished_message,
-				 size_t tls_finished_len)
+				 size_t tls_finished_len,
+				 char *certificate_hash,
+				 size_t certificate_hash_len)
 {
 	fe_scram_state *state;
 	char	   *prep_password;
@@ -112,6 +116,8 @@ pg_fe_scram_init(const char *username,
 	state->ssl_in_use = ssl_in_use;
 	state->tls_finished_message = tls_finished_message;
 	state->tls_finished_len = tls_finished_len;
+	state->certificate_hash = certificate_hash;
+	state->certificate_hash_len = certificate_hash_len;
 	state->sasl_mechanism = strdup(sasl_mechanism);
 	state->channel_binding_type = channel_binding_type;
 
@@ -156,8 +162,8 @@ pg_fe_scram_free(void *opaq)
 		free(state->password);
 	if (state->tls_finished_message)
 		free(state->tls_finished_message);
-	if (state->sasl_mechanism)
-		free(state->sasl_mechanism);
+	if (state->certificate_hash)
+		free(state->certificate_hash);
 
 	/* client messages */
 	if (state->client_nonce)
@@ -461,6 +467,12 @@ build_client_final_message(fe_scram_state *state, PQExpBuffer errormessage)
 			cbind_data = state->tls_finished_message;
 			cbind_data_len = state->tls_finished_len;
 		}
+		else if (strcmp(state->channel_binding_type,
+						SCRAM_CHANNEL_BINDING_TLS_ENDPOINT) == 0)
+		{
+			cbind_data = state->certificate_hash;
+			cbind_data_len = state->certificate_hash_len;
+		}
 		else
 		{
 			/* should not happen */
diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c
index 3340a9ad93..bb9b0573d1 100644
--- a/src/interfaces/libpq/fe-auth.c
+++ b/src/interfaces/libpq/fe-auth.c
@@ -493,6 +493,8 @@ pg_SASL_init(PGconn *conn, int payloadlen)
 	PQExpBufferData mechanism_buf;
 	char	   *tls_finished = NULL;
 	size_t		tls_finished_len = 0;
+	char	   *certificate_hash = NULL;
+	size_t		certificate_hash_len = 0;
 	char	   *password;
 
 	initPQExpBuffer(&mechanism_buf);
@@ -580,6 +582,12 @@ pg_SASL_init(PGconn *conn, int payloadlen)
 		tls_finished = pgtls_get_finished(conn, &tls_finished_len);
 		if (tls_finished == NULL)
 			goto oom_error;
+
+		certificate_hash =
+			pgtls_get_peer_certificate_hash(conn,
+											&certificate_hash_len);
+		if (certificate_hash == NULL)
+			goto error;		/* error message is set */
 	}
 #endif
 
@@ -595,7 +603,9 @@ pg_SASL_init(PGconn *conn, int payloadlen)
 										selected_mechanism,
 										conn->scram_channel_binding,
 										tls_finished,
-										tls_finished_len);
+										tls_finished_len,
+										certificate_hash,
+										certificate_hash_len);
 	if (!conn->sasl_state)
 		goto oom_error;
 
diff --git a/src/interfaces/libpq/fe-auth.h b/src/interfaces/libpq/fe-auth.h
index db319ac071..68de8b6e32 100644
--- a/src/interfaces/libpq/fe-auth.h
+++ b/src/interfaces/libpq/fe-auth.h
@@ -29,7 +29,9 @@ extern void *pg_fe_scram_init(const char *username,
 				 const char *sasl_mechanism,
 				 const char *channel_binding_type,
 				 char *tls_finished_message,
-				 size_t tls_finished_len);
+				 size_t tls_finished_len,
+				 char *certificate_hash,
+				 size_t certificate_hash_len);
 extern void pg_fe_scram_free(void *opaq);
 extern void pg_fe_scram_exchange(void *opaq, char *input, int inputlen,
 					 char **output, int *outputlen,
diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
index 61d161b367..99077c3d9a 100644
--- a/src/interfaces/libpq/fe-secure-openssl.c
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -419,6 +419,84 @@ pgtls_get_finished(PGconn *conn, size_t *len)
 	return result;
 }
 
+/*
+ *	Get the hash of the server certificate
+ *
+ * This information is useful for end-point channel binding, where the
+ * client certificate hash is used as a link, per RFC 5929. If the
+ * signature hash algorithm is MD5 or SHA-1, fall back to SHA-256,
+ * as per RFC 5929 (https://tools.ietf.org/html/rfc5929#section-4.1).
+ * NULL is sent back to the caller in the event of an error, with an
+ * error message for the caller to consume.
+ */
+char *
+pgtls_get_peer_certificate_hash(PGconn *conn, size_t *len)
+{
+	char	   *cert_hash = NULL;
+
+	*len = 0;
+
+	if (conn->peer)
+	{
+		X509		   *peer_cert = conn->peer;
+		const EVP_MD   *algo_type = NULL;
+		char			hash[EVP_MAX_MD_SIZE];	/* size for SHA-512 */
+		unsigned int	hash_size;
+		int				algo_nid;
+
+		/*
+		 * Get the signature algorithm of the certificate to determine the
+		 * hash algorithm to use for the result.
+		 */
+		if (!OBJ_find_sigid_algs(X509_get_signature_nid(peer_cert),
+								 &algo_nid, NULL))
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("could not find signature algorithm\n"));
+			return NULL;
+		}
+
+		switch (algo_nid)
+		{
+			case NID_md5:
+			case NID_sha1:
+				algo_type = EVP_sha256();
+				break;
+
+			default:
+				algo_type = EVP_get_digestbynid(algo_nid);
+				if (algo_type == NULL)
+				{
+					printfPQExpBuffer(&conn->errorMessage,
+									  libpq_gettext("could not find digest for NID %s\n"),
+									  OBJ_nid2sn(algo_nid));
+					return NULL;
+				}
+				break;
+		}
+
+		if (!X509_digest(peer_cert, algo_type, (unsigned char *) hash,
+						 &hash_size))
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("could not generate peer certificate hash\n"));
+			return NULL;
+		}
+
+		/* save result */
+		cert_hash = (char *) malloc(hash_size);
+		if (cert_hash == NULL)
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("out of memory\n"));
+			return NULL;
+		}
+		memcpy(cert_hash, hash, hash_size);
+		*len = hash_size;
+	}
+
+	return cert_hash;
+}
 
 /* ------------------------------------------------------------ */
 /*						OpenSSL specific code					*/
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index f6c1023f37..756c4d61e1 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -672,6 +672,7 @@ extern ssize_t pgtls_read(PGconn *conn, void *ptr, size_t len);
 extern bool pgtls_read_pending(PGconn *conn);
 extern ssize_t pgtls_write(PGconn *conn, const void *ptr, size_t len);
 extern char *pgtls_get_finished(PGconn *conn, size_t *len);
+extern char *pgtls_get_peer_certificate_hash(PGconn *conn, size_t *len);
 
 /*
  * this is so that we can check if a connection is non-blocking internally
diff --git a/src/test/ssl/t/002_scram.pl b/src/test/ssl/t/002_scram.pl
index 324b4888d4..3f425e00f0 100644
--- a/src/test/ssl/t/002_scram.pl
+++ b/src/test/ssl/t/002_scram.pl
@@ -4,7 +4,7 @@ use strict;
 use warnings;
 use PostgresNode;
 use TestLib;
-use Test::More tests => 4;
+use Test::More tests => 5;
 use ServerSetup;
 use File::Copy;
 
@@ -45,6 +45,9 @@ test_connect_ok($common_connstr,
 test_connect_ok($common_connstr,
 	"scram_channel_binding=''",
 	"SCRAM authentication without channel binding");
+test_connect_ok($common_connstr,
+	"scram_channel_binding=tls-server-end-point",
+	"SCRAM authentication with tls-server-end-point as channel binding");
 test_connect_fails($common_connstr,
 	"scram_channel_binding=not-exists",
 	"SCRAM authentication with invalid channel binding");
-- 
2.15.1

From 006093fc8bf27e1bfe142dbcd79dadea4257909f Mon Sep 17 00:00:00 2001
From: Michael Paquier <mich...@paquier.xyz>
Date: Fri, 22 Dec 2017 17:04:19 +0900
Subject: [PATCH 2/2] Refactor channel binding code to fetch cbind_data only
 when necessary

As things stand now, channel binding data is fetched from OpenSSL and saved
into the SASL exchange context for any SSL connection attempted for a SCRAM
authentication, resulting in data fetched but not used if no channel binding
is used or if a different channel binding type is used than what the data
is here for.

Refactor the code in such a way that binding data is only fetched from the
SSL stack only when a specific channel binding is used for both the frontend
and the backend. In order to achieve that, save the libpq connection context
directly in the SCRAM exchange state, and add a dependency to SSL in the
low-level SCRAM routines.

This makes the interface in charge of initializing the SCRAM context cleaner
as all its data comes from either PGconn* (for frontend) or Port* (for the
backend).
---
 src/backend/libpq/auth-scram.c       |  47 ++++-----
 src/backend/libpq/auth.c             |  25 +----
 src/include/libpq/scram.h            |   7 +-
 src/interfaces/libpq/fe-auth-scram.c | 180 ++++++++++++++++++-----------------
 src/interfaces/libpq/fe-auth.c       |  37 +------
 src/interfaces/libpq/fe-auth.h       |  12 +--
 6 files changed, 121 insertions(+), 187 deletions(-)

diff --git a/src/backend/libpq/auth-scram.c b/src/backend/libpq/auth-scram.c
index 849587d141..0a50f815ab 100644
--- a/src/backend/libpq/auth-scram.c
+++ b/src/backend/libpq/auth-scram.c
@@ -110,12 +110,8 @@ typedef struct
 
 	const char *username;		/* username from startup packet */
 
+	Port	   *port;
 	char		cbind_flag;
-	bool		ssl_in_use;
-	const char *tls_finished_message;
-	size_t		tls_finished_len;
-	const char *certificate_hash;
-	size_t		certificate_hash_len;
 	char	   *channel_binding_type;
 
 	int			iterations;
@@ -174,25 +170,15 @@ static char *scram_mock_salt(const char *username);
  * it will fail, as if an incorrect password was given.
  */
 void *
-pg_be_scram_init(const char *username,
-				 const char *shadow_pass,
-				 bool ssl_in_use,
-				 const char *tls_finished_message,
-				 size_t tls_finished_len,
-				 const char *certificate_hash,
-				 size_t certificate_hash_len)
+pg_be_scram_init(Port *port,
+				 const char *shadow_pass)
 {
 	scram_state *state;
 	bool		got_verifier;
 
 	state = (scram_state *) palloc0(sizeof(scram_state));
+	state->port = port;
 	state->state = SCRAM_AUTH_INIT;
-	state->username = username;
-	state->ssl_in_use = ssl_in_use;
-	state->tls_finished_message = tls_finished_message;
-	state->tls_finished_len = tls_finished_len;
-	state->certificate_hash = certificate_hash;
-	state->certificate_hash_len = certificate_hash_len;
 	state->channel_binding_type = NULL;
 
 	/*
@@ -215,7 +201,7 @@ pg_be_scram_init(const char *username,
 				 */
 				ereport(LOG,
 						(errmsg("invalid SCRAM verifier for user \"%s\"",
-								username)));
+								state->port->user_name)));
 				got_verifier = false;
 			}
 		}
@@ -226,7 +212,7 @@ pg_be_scram_init(const char *username,
 			 * authentication with an MD5 hash.)
 			 */
 			state->logdetail = psprintf(_("User \"%s\" does not have a valid SCRAM verifier."),
-										state->username);
+										state->port->user_name);
 			got_verifier = false;
 		}
 	}
@@ -248,8 +234,8 @@ pg_be_scram_init(const char *username,
 	 */
 	if (!got_verifier)
 	{
-		mock_scram_verifier(username, &state->iterations, &state->salt,
-							state->StoredKey, state->ServerKey);
+		mock_scram_verifier(state->port->user_name, &state->iterations,
+							&state->salt, state->StoredKey, state->ServerKey);
 		state->doomed = true;
 	}
 
@@ -821,7 +807,7 @@ read_client_first_message(scram_state *state, char *input)
 			 * it supports channel binding, which in this implementation is
 			 * the case if a connection is using SSL.
 			 */
-			if (state->ssl_in_use)
+			if (state->port->ssl_in_use)
 				ereport(ERROR,
 						(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
 						 errmsg("SCRAM channel binding negotiation error"),
@@ -845,7 +831,7 @@ read_client_first_message(scram_state *state, char *input)
 			{
 				char	   *channel_binding_type;
 
-				if (!state->ssl_in_use)
+				if (!state->port->ssl_in_use)
 				{
 					/*
 					 * Without SSL, we don't support channel binding.
@@ -1128,14 +1114,19 @@ read_client_final_message(scram_state *state, char *input)
 		 */
 		if (strcmp(state->channel_binding_type, SCRAM_CHANNEL_BINDING_TLS_UNIQUE) == 0)
 		{
-			cbind_data = state->tls_finished_message;
-			cbind_data_len = state->tls_finished_len;
+			/* Fetch data from TLS finished message */
+#ifdef USE_SSL
+			cbind_data = be_tls_get_peer_finished(state->port, &cbind_data_len);
+#endif
 		}
 		else if (strcmp(state->channel_binding_type,
 						SCRAM_CHANNEL_BINDING_TLS_ENDPOINT) == 0)
 		{
-			cbind_data = state->certificate_hash;
-			cbind_data_len = state->certificate_hash_len;
+			/* Fetch hash data of server's SSL certificate */
+#ifdef USE_SSL
+			cbind_data = be_tls_get_certificate_hash(state->port,
+													 &cbind_data_len);
+#endif
 		}
 		else
 		{
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 700a3bffa4..bd91e1cd18 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -873,10 +873,6 @@ CheckSCRAMAuth(Port *port, char *shadow_pass, char **logdetail)
 	int			inputlen;
 	int			result;
 	bool		initial;
-	char	   *tls_finished = NULL;
-	size_t		tls_finished_len = 0;
-	char	   *certificate_hash = NULL;
-	size_t		certificate_hash_len = 0;
 
 	/*
 	 * SASL auth is not supported for protocol versions before 3, because it
@@ -917,19 +913,6 @@ CheckSCRAMAuth(Port *port, char *shadow_pass, char **logdetail)
 	sendAuthRequest(port, AUTH_REQ_SASL, sasl_mechs, p - sasl_mechs + 1);
 	pfree(sasl_mechs);
 
-#ifdef USE_SSL
-
-	/*
-	 * Get data for channel binding.
-	 */
-	if (port->ssl_in_use)
-	{
-		tls_finished = be_tls_get_peer_finished(port, &tls_finished_len);
-		certificate_hash = be_tls_get_certificate_hash(port,
-													   &certificate_hash_len);
-	}
-#endif
-
 	/*
 	 * Initialize the status tracker for message exchanges.
 	 *
@@ -941,13 +924,7 @@ CheckSCRAMAuth(Port *port, char *shadow_pass, char **logdetail)
 	 * This is because we don't want to reveal to an attacker what usernames
 	 * are valid, nor which users have a valid password.
 	 */
-	scram_opaq = pg_be_scram_init(port->user_name,
-								  shadow_pass,
-								  port->ssl_in_use,
-								  tls_finished,
-								  tls_finished_len,
-								  certificate_hash,
-								  certificate_hash_len);
+	scram_opaq = pg_be_scram_init(port, shadow_pass);
 
 	/*
 	 * Loop through SASL message exchange.  This exchange can consist of
diff --git a/src/include/libpq/scram.h b/src/include/libpq/scram.h
index 7c8f009a3b..f404f57253 100644
--- a/src/include/libpq/scram.h
+++ b/src/include/libpq/scram.h
@@ -13,16 +13,15 @@
 #ifndef PG_SCRAM_H
 #define PG_SCRAM_H
 
+#include "libpq/libpq-be.h"
+
 /* Status codes for message exchange */
 #define SASL_EXCHANGE_CONTINUE		0
 #define SASL_EXCHANGE_SUCCESS		1
 #define SASL_EXCHANGE_FAILURE		2
 
 /* Routines dedicated to authentication */
-extern void *pg_be_scram_init(const char *username, const char *shadow_pass,
-				 bool ssl_in_use, const char *tls_finished_message,
-				 size_t tls_finished_len, const char *certificate_hash,
-				 size_t certificate_hash_len);
+extern void *pg_be_scram_init(Port *port, const char *shadow_pass);
 extern int pg_be_scram_exchange(void *opaq, char *input, int inputlen,
 					 char **output, int *outputlen, char **logdetail);
 
diff --git a/src/interfaces/libpq/fe-auth-scram.c b/src/interfaces/libpq/fe-auth-scram.c
index a56fccf12e..65817411b1 100644
--- a/src/interfaces/libpq/fe-auth-scram.c
+++ b/src/interfaces/libpq/fe-auth-scram.c
@@ -42,15 +42,9 @@ typedef struct
 	fe_scram_state_enum state;
 
 	/* These are supplied by the user */
-	const char *username;
+	PGconn	   *conn;
 	char	   *password;
-	bool		ssl_in_use;
-	char	   *tls_finished_message;
-	size_t		tls_finished_len;
-	char	   *certificate_hash;
-	size_t		certificate_hash_len;
 	char	   *sasl_mechanism;
-	const char *channel_binding_type;
 
 	/* We construct these */
 	uint8		SaltedPassword[SCRAM_KEY_LEN];
@@ -70,14 +64,10 @@ typedef struct
 	char		ServerSignature[SCRAM_KEY_LEN];
 } fe_scram_state;
 
-static bool read_server_first_message(fe_scram_state *state, char *input,
-						  PQExpBuffer errormessage);
-static bool read_server_final_message(fe_scram_state *state, char *input,
-						  PQExpBuffer errormessage);
-static char *build_client_first_message(fe_scram_state *state,
-						   PQExpBuffer errormessage);
-static char *build_client_final_message(fe_scram_state *state,
-						   PQExpBuffer errormessage);
+static bool read_server_first_message(fe_scram_state *state, char *input);
+static bool read_server_final_message(fe_scram_state *state, char *input);
+static char *build_client_first_message(fe_scram_state *state);
+static char *build_client_final_message(fe_scram_state *state);
 static bool verify_server_signature(fe_scram_state *state);
 static void calculate_client_proof(fe_scram_state *state,
 					   const char *client_final_message_without_proof,
@@ -91,15 +81,9 @@ static bool pg_frontend_random(char *dst, int len);
  * freed by pg_fe_scram_free().
  */
 void *
-pg_fe_scram_init(const char *username,
+pg_fe_scram_init(PGconn *conn,
 				 const char *password,
-				 bool ssl_in_use,
-				 const char *sasl_mechanism,
-				 const char *channel_binding_type,
-				 char *tls_finished_message,
-				 size_t tls_finished_len,
-				 char *certificate_hash,
-				 size_t certificate_hash_len)
+				 const char *sasl_mechanism)
 {
 	fe_scram_state *state;
 	char	   *prep_password;
@@ -111,15 +95,9 @@ pg_fe_scram_init(const char *username,
 	if (!state)
 		return NULL;
 	memset(state, 0, sizeof(fe_scram_state));
+	state->conn = conn;
 	state->state = FE_SCRAM_INIT;
-	state->username = username;
-	state->ssl_in_use = ssl_in_use;
-	state->tls_finished_message = tls_finished_message;
-	state->tls_finished_len = tls_finished_len;
-	state->certificate_hash = certificate_hash;
-	state->certificate_hash_len = certificate_hash_len;
 	state->sasl_mechanism = strdup(sasl_mechanism);
-	state->channel_binding_type = channel_binding_type;
 
 	if (!state->sasl_mechanism)
 	{
@@ -160,10 +138,6 @@ pg_fe_scram_free(void *opaq)
 
 	if (state->password)
 		free(state->password);
-	if (state->tls_finished_message)
-		free(state->tls_finished_message);
-	if (state->certificate_hash)
-		free(state->certificate_hash);
 
 	/* client messages */
 	if (state->client_nonce)
@@ -194,9 +168,10 @@ pg_fe_scram_free(void *opaq)
 void
 pg_fe_scram_exchange(void *opaq, char *input, int inputlen,
 					 char **output, int *outputlen,
-					 bool *done, bool *success, PQExpBuffer errorMessage)
+					 bool *done, bool *success)
 {
 	fe_scram_state *state = (fe_scram_state *) opaq;
+	PGconn	   *conn = state->conn;
 
 	*done = false;
 	*success = false;
@@ -211,13 +186,13 @@ pg_fe_scram_exchange(void *opaq, char *input, int inputlen,
 	{
 		if (inputlen == 0)
 		{
-			printfPQExpBuffer(errorMessage,
+			printfPQExpBuffer(&conn->errorMessage,
 							  libpq_gettext("malformed SCRAM message (empty message)\n"));
 			goto error;
 		}
 		if (inputlen != strlen(input))
 		{
-			printfPQExpBuffer(errorMessage,
+			printfPQExpBuffer(&conn->errorMessage,
 							  libpq_gettext("malformed SCRAM message (length mismatch)\n"));
 			goto error;
 		}
@@ -227,7 +202,7 @@ pg_fe_scram_exchange(void *opaq, char *input, int inputlen,
 	{
 		case FE_SCRAM_INIT:
 			/* Begin the SCRAM handshake, by sending client nonce */
-			*output = build_client_first_message(state, errorMessage);
+			*output = build_client_first_message(state);
 			if (*output == NULL)
 				goto error;
 
@@ -238,10 +213,10 @@ pg_fe_scram_exchange(void *opaq, char *input, int inputlen,
 
 		case FE_SCRAM_NONCE_SENT:
 			/* Receive salt and server nonce, send response. */
-			if (!read_server_first_message(state, input, errorMessage))
+			if (!read_server_first_message(state, input))
 				goto error;
 
-			*output = build_client_final_message(state, errorMessage);
+			*output = build_client_final_message(state);
 			if (*output == NULL)
 				goto error;
 
@@ -252,7 +227,7 @@ pg_fe_scram_exchange(void *opaq, char *input, int inputlen,
 
 		case FE_SCRAM_PROOF_SENT:
 			/* Receive server signature */
-			if (!read_server_final_message(state, input, errorMessage))
+			if (!read_server_final_message(state, input))
 				goto error;
 
 			/*
@@ -266,7 +241,7 @@ pg_fe_scram_exchange(void *opaq, char *input, int inputlen,
 			else
 			{
 				*success = false;
-				printfPQExpBuffer(errorMessage,
+				printfPQExpBuffer(&conn->errorMessage,
 								  libpq_gettext("incorrect server signature\n"));
 			}
 			*done = true;
@@ -275,7 +250,7 @@ pg_fe_scram_exchange(void *opaq, char *input, int inputlen,
 
 		default:
 			/* shouldn't happen */
-			printfPQExpBuffer(errorMessage,
+			printfPQExpBuffer(&conn->errorMessage,
 							  libpq_gettext("invalid SCRAM exchange state\n"));
 			goto error;
 	}
@@ -333,8 +308,9 @@ read_attr_value(char **input, char attr, PQExpBuffer errorMessage)
  * Build the first exchange message sent by the client.
  */
 static char *
-build_client_first_message(fe_scram_state *state, PQExpBuffer errormessage)
+build_client_first_message(fe_scram_state *state)
 {
+	PGconn	   *conn = state->conn;
 	char		raw_nonce[SCRAM_RAW_NONCE_LEN + 1];
 	char	   *result;
 	int			channel_info_len;
@@ -347,7 +323,7 @@ build_client_first_message(fe_scram_state *state, PQExpBuffer errormessage)
 	 */
 	if (!pg_frontend_random(raw_nonce, SCRAM_RAW_NONCE_LEN))
 	{
-		printfPQExpBuffer(errormessage,
+		printfPQExpBuffer(&conn->errorMessage,
 						  libpq_gettext("could not generate nonce\n"));
 		return NULL;
 	}
@@ -355,7 +331,7 @@ build_client_first_message(fe_scram_state *state, PQExpBuffer errormessage)
 	state->client_nonce = malloc(pg_b64_enc_len(SCRAM_RAW_NONCE_LEN) + 1);
 	if (state->client_nonce == NULL)
 	{
-		printfPQExpBuffer(errormessage,
+		printfPQExpBuffer(&conn->errorMessage,
 						  libpq_gettext("out of memory\n"));
 		return NULL;
 	}
@@ -376,11 +352,11 @@ build_client_first_message(fe_scram_state *state, PQExpBuffer errormessage)
 	 */
 	if (strcmp(state->sasl_mechanism, SCRAM_SHA256_PLUS_NAME) == 0)
 	{
-		Assert(state->ssl_in_use);
-		appendPQExpBuffer(&buf, "p=%s", state->channel_binding_type);
+		Assert(conn->ssl_in_use);
+		appendPQExpBuffer(&buf, "p=%s", conn->scram_channel_binding);
 	}
-	else if (state->channel_binding_type == NULL ||
-			 strlen(state->channel_binding_type) == 0)
+	else if (conn->scram_channel_binding == NULL ||
+			 strlen(conn->scram_channel_binding) == 0)
 	{
 		/*
 		 * Client has chosen to not show to server that it supports channel
@@ -388,7 +364,7 @@ build_client_first_message(fe_scram_state *state, PQExpBuffer errormessage)
 		 */
 		appendPQExpBuffer(&buf, "n");
 	}
-	else if (state->ssl_in_use)
+	else if (conn->ssl_in_use)
 	{
 		/*
 		 * Client supports channel binding, but thinks the server does not.
@@ -429,7 +405,7 @@ build_client_first_message(fe_scram_state *state, PQExpBuffer errormessage)
 
 oom_error:
 	termPQExpBuffer(&buf);
-	printfPQExpBuffer(errormessage,
+	printfPQExpBuffer(&conn->errorMessage,
 					  libpq_gettext("out of memory\n"));
 	return NULL;
 }
@@ -438,9 +414,10 @@ oom_error:
  * Build the final exchange message sent from the client.
  */
 static char *
-build_client_final_message(fe_scram_state *state, PQExpBuffer errormessage)
+build_client_final_message(fe_scram_state *state)
 {
 	PQExpBufferData buf;
+	PGconn	   *conn = state->conn;
 	uint8		client_proof[SCRAM_KEY_LEN];
 	char	   *result;
 
@@ -456,28 +433,41 @@ build_client_final_message(fe_scram_state *state, PQExpBuffer errormessage)
 	 */
 	if (strcmp(state->sasl_mechanism, SCRAM_SHA256_PLUS_NAME) == 0)
 	{
-		char	   *cbind_data;
-		size_t		cbind_data_len;
+		char	   *cbind_data = NULL;
+		size_t		cbind_data_len = 0;
 		size_t		cbind_header_len;
 		char	   *cbind_input;
 		size_t		cbind_input_len;
 
-		if (strcmp(state->channel_binding_type, SCRAM_CHANNEL_BINDING_TLS_UNIQUE) == 0)
+		if (strcmp(conn->scram_channel_binding, SCRAM_CHANNEL_BINDING_TLS_UNIQUE) == 0)
 		{
-			cbind_data = state->tls_finished_message;
-			cbind_data_len = state->tls_finished_len;
+			/* Fetch data from TLS finished message */
+#ifdef USE_SSL
+			cbind_data = pgtls_get_finished(state->conn, &cbind_data_len);
+			if (cbind_data == NULL)
+				goto oom_error;
+#endif
 		}
-		else if (strcmp(state->channel_binding_type,
+		else if (strcmp(conn->scram_channel_binding,
 						SCRAM_CHANNEL_BINDING_TLS_ENDPOINT) == 0)
 		{
-			cbind_data = state->certificate_hash;
-			cbind_data_len = state->certificate_hash_len;
+			/* Fetch hash data of server's SSL certificate */
+#ifdef USE_SSL
+			cbind_data =
+				pgtls_get_peer_certificate_hash(state->conn,
+												&cbind_data_len);
+			if (cbind_data == NULL)
+			{
+				/* error message is already set on error */
+				return NULL;
+			}
+#endif
 		}
 		else
 		{
 			/* should not happen */
 			termPQExpBuffer(&buf);
-			printfPQExpBuffer(errormessage,
+			printfPQExpBuffer(&conn->errorMessage,
 							  libpq_gettext("invalid channel binding type\n"));
 			return NULL;
 		}
@@ -485,37 +475,46 @@ build_client_final_message(fe_scram_state *state, PQExpBuffer errormessage)
 		/* should not happen */
 		if (cbind_data == NULL || cbind_data_len == 0)
 		{
+			if (cbind_data != NULL)
+				free(cbind_data);
 			termPQExpBuffer(&buf);
-			printfPQExpBuffer(errormessage,
+			printfPQExpBuffer(&conn->errorMessage,
 							  libpq_gettext("empty channel binding data for channel binding type \"%s\"\n"),
-							  state->channel_binding_type);
+							  conn->scram_channel_binding);
 			return NULL;
 		}
 
 		appendPQExpBuffer(&buf, "c=");
 
-		cbind_header_len = 4 + strlen(state->channel_binding_type); /* p=type,, */
+		/* p=type,, */
+		cbind_header_len = 4 + strlen(conn->scram_channel_binding);
 		cbind_input_len = cbind_header_len + cbind_data_len;
 		cbind_input = malloc(cbind_input_len);
 		if (!cbind_input)
+		{
+			free(cbind_data);
 			goto oom_error;
-		snprintf(cbind_input, cbind_input_len, "p=%s,,", state->channel_binding_type);
+		}
+		snprintf(cbind_input, cbind_input_len, "p=%s,,",
+				 conn->scram_channel_binding);
 		memcpy(cbind_input + cbind_header_len, cbind_data, cbind_data_len);
 
 		if (!enlargePQExpBuffer(&buf, pg_b64_enc_len(cbind_input_len)))
 		{
+			free(cbind_data);
 			free(cbind_input);
 			goto oom_error;
 		}
 		buf.len += pg_b64_encode(cbind_input, cbind_input_len, buf.data + buf.len);
 		buf.data[buf.len] = '\0';
 
+		free(cbind_data);
 		free(cbind_input);
 	}
-	else if (state->channel_binding_type == NULL ||
-			 strlen(state->channel_binding_type) == 0)
+	else if (conn->scram_channel_binding == NULL ||
+			 strlen(conn->scram_channel_binding) == 0)
 		appendPQExpBuffer(&buf, "c=biws");	/* base64 of "n,," */
-	else if (state->ssl_in_use)
+	else if (conn->ssl_in_use)
 		appendPQExpBuffer(&buf, "c=eSws");	/* base64 of "y,," */
 	else
 		appendPQExpBuffer(&buf, "c=biws");	/* base64 of "n,," */
@@ -553,7 +552,7 @@ build_client_final_message(fe_scram_state *state, PQExpBuffer errormessage)
 
 oom_error:
 	termPQExpBuffer(&buf);
-	printfPQExpBuffer(errormessage,
+	printfPQExpBuffer(&conn->errorMessage,
 					  libpq_gettext("out of memory\n"));
 	return NULL;
 }
@@ -562,9 +561,9 @@ oom_error:
  * Read the first exchange message coming from the server.
  */
 static bool
-read_server_first_message(fe_scram_state *state, char *input,
-						  PQExpBuffer errormessage)
+read_server_first_message(fe_scram_state *state, char *input)
 {
+	PGconn	   *conn = state->conn;
 	char	   *iterations_str;
 	char	   *endptr;
 	char	   *encoded_salt;
@@ -573,13 +572,14 @@ read_server_first_message(fe_scram_state *state, char *input,
 	state->server_first_message = strdup(input);
 	if (state->server_first_message == NULL)
 	{
-		printfPQExpBuffer(errormessage,
+		printfPQExpBuffer(&conn->errorMessage,
 						  libpq_gettext("out of memory\n"));
 		return false;
 	}
 
 	/* parse the message */
-	nonce = read_attr_value(&input, 'r', errormessage);
+	nonce = read_attr_value(&input, 'r',
+							&conn->errorMessage);
 	if (nonce == NULL)
 	{
 		/* read_attr_value() has generated an error string */
@@ -590,7 +590,7 @@ read_server_first_message(fe_scram_state *state, char *input,
 	if (strlen(nonce) < strlen(state->client_nonce) ||
 		memcmp(nonce, state->client_nonce, strlen(state->client_nonce)) != 0)
 	{
-		printfPQExpBuffer(errormessage,
+		printfPQExpBuffer(&conn->errorMessage,
 						  libpq_gettext("invalid SCRAM response (nonce mismatch)\n"));
 		return false;
 	}
@@ -598,12 +598,12 @@ read_server_first_message(fe_scram_state *state, char *input,
 	state->nonce = strdup(nonce);
 	if (state->nonce == NULL)
 	{
-		printfPQExpBuffer(errormessage,
+		printfPQExpBuffer(&conn->errorMessage,
 						  libpq_gettext("out of memory\n"));
 		return false;
 	}
 
-	encoded_salt = read_attr_value(&input, 's', errormessage);
+	encoded_salt = read_attr_value(&input, 's', &conn->errorMessage);
 	if (encoded_salt == NULL)
 	{
 		/* read_attr_value() has generated an error string */
@@ -612,7 +612,7 @@ read_server_first_message(fe_scram_state *state, char *input,
 	state->salt = malloc(pg_b64_dec_len(strlen(encoded_salt)));
 	if (state->salt == NULL)
 	{
-		printfPQExpBuffer(errormessage,
+		printfPQExpBuffer(&conn->errorMessage,
 						  libpq_gettext("out of memory\n"));
 		return false;
 	}
@@ -620,7 +620,7 @@ read_server_first_message(fe_scram_state *state, char *input,
 								   strlen(encoded_salt),
 								   state->salt);
 
-	iterations_str = read_attr_value(&input, 'i', errormessage);
+	iterations_str = read_attr_value(&input, 'i', &conn->errorMessage);
 	if (iterations_str == NULL)
 	{
 		/* read_attr_value() has generated an error string */
@@ -629,13 +629,13 @@ read_server_first_message(fe_scram_state *state, char *input,
 	state->iterations = strtol(iterations_str, &endptr, 10);
 	if (*endptr != '\0' || state->iterations < 1)
 	{
-		printfPQExpBuffer(errormessage,
+		printfPQExpBuffer(&conn->errorMessage,
 						  libpq_gettext("malformed SCRAM message (invalid iteration count)\n"));
 		return false;
 	}
 
 	if (*input != '\0')
-		printfPQExpBuffer(errormessage,
+		printfPQExpBuffer(&conn->errorMessage,
 						  libpq_gettext("malformed SCRAM message (garbage at end of server-first-message)\n"));
 
 	return true;
@@ -645,16 +645,16 @@ read_server_first_message(fe_scram_state *state, char *input,
  * Read the final exchange message coming from the server.
  */
 static bool
-read_server_final_message(fe_scram_state *state, char *input,
-						  PQExpBuffer errormessage)
+read_server_final_message(fe_scram_state *state, char *input)
 {
+	PGconn	   *conn = state->conn;
 	char	   *encoded_server_signature;
 	int			server_signature_len;
 
 	state->server_final_message = strdup(input);
 	if (!state->server_final_message)
 	{
-		printfPQExpBuffer(errormessage,
+		printfPQExpBuffer(&conn->errorMessage,
 						  libpq_gettext("out of memory\n"));
 		return false;
 	}
@@ -662,16 +662,18 @@ read_server_final_message(fe_scram_state *state, char *input,
 	/* Check for error result. */
 	if (*input == 'e')
 	{
-		char	   *errmsg = read_attr_value(&input, 'e', errormessage);
+		char	   *errmsg = read_attr_value(&input, 'e',
+											 &conn->errorMessage);
 
-		printfPQExpBuffer(errormessage,
+		printfPQExpBuffer(&conn->errorMessage,
 						  libpq_gettext("error received from server in SCRAM exchange: %s\n"),
 						  errmsg);
 		return false;
 	}
 
 	/* Parse the message. */
-	encoded_server_signature = read_attr_value(&input, 'v', errormessage);
+	encoded_server_signature = read_attr_value(&input, 'v',
+											   &conn->errorMessage);
 	if (encoded_server_signature == NULL)
 	{
 		/* read_attr_value() has generated an error message */
@@ -679,7 +681,7 @@ read_server_final_message(fe_scram_state *state, char *input,
 	}
 
 	if (*input != '\0')
-		printfPQExpBuffer(errormessage,
+		printfPQExpBuffer(&conn->errorMessage,
 						  libpq_gettext("malformed SCRAM message (garbage at end of server-final-message)\n"));
 
 	server_signature_len = pg_b64_decode(encoded_server_signature,
@@ -687,7 +689,7 @@ read_server_final_message(fe_scram_state *state, char *input,
 										 state->ServerSignature);
 	if (server_signature_len != SCRAM_KEY_LEN)
 	{
-		printfPQExpBuffer(errormessage,
+		printfPQExpBuffer(&conn->errorMessage,
 						  libpq_gettext("malformed SCRAM message (invalid server signature)\n"));
 		return false;
 	}
diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c
index bb9b0573d1..9c3524e553 100644
--- a/src/interfaces/libpq/fe-auth.c
+++ b/src/interfaces/libpq/fe-auth.c
@@ -491,10 +491,6 @@ pg_SASL_init(PGconn *conn, int payloadlen)
 	bool		success;
 	const char *selected_mechanism;
 	PQExpBufferData mechanism_buf;
-	char	   *tls_finished = NULL;
-	size_t		tls_finished_len = 0;
-	char	   *certificate_hash = NULL;
-	size_t		certificate_hash_len = 0;
 	char	   *password;
 
 	initPQExpBuffer(&mechanism_buf);
@@ -572,40 +568,15 @@ pg_SASL_init(PGconn *conn, int payloadlen)
 		goto error;
 	}
 
-#ifdef USE_SSL
-
-	/*
-	 * Get data for channel binding.
-	 */
-	if (strcmp(selected_mechanism, SCRAM_SHA256_PLUS_NAME) == 0)
-	{
-		tls_finished = pgtls_get_finished(conn, &tls_finished_len);
-		if (tls_finished == NULL)
-			goto oom_error;
-
-		certificate_hash =
-			pgtls_get_peer_certificate_hash(conn,
-											&certificate_hash_len);
-		if (certificate_hash == NULL)
-			goto error;		/* error message is set */
-	}
-#endif
-
 	/*
 	 * Initialize the SASL state information with all the information gathered
 	 * during the initial exchange.
 	 *
 	 * Note: Only tls-unique is supported for the moment.
 	 */
-	conn->sasl_state = pg_fe_scram_init(conn->pguser,
+	conn->sasl_state = pg_fe_scram_init(conn,
 										password,
-										conn->ssl_in_use,
-										selected_mechanism,
-										conn->scram_channel_binding,
-										tls_finished,
-										tls_finished_len,
-										certificate_hash,
-										certificate_hash_len);
+										selected_mechanism);
 	if (!conn->sasl_state)
 		goto oom_error;
 
@@ -613,7 +584,7 @@ pg_SASL_init(PGconn *conn, int payloadlen)
 	pg_fe_scram_exchange(conn->sasl_state,
 						 NULL, -1,
 						 &initialresponse, &initialresponselen,
-						 &done, &success, &conn->errorMessage);
+						 &done, &success);
 
 	if (done && !success)
 		goto error;
@@ -694,7 +665,7 @@ pg_SASL_continue(PGconn *conn, int payloadlen, bool final)
 	pg_fe_scram_exchange(conn->sasl_state,
 						 challenge, payloadlen,
 						 &output, &outputlen,
-						 &done, &success, &conn->errorMessage);
+						 &done, &success);
 	free(challenge);			/* don't need the input anymore */
 
 	if (final && !done)
diff --git a/src/interfaces/libpq/fe-auth.h b/src/interfaces/libpq/fe-auth.h
index 68de8b6e32..1265d0d2f7 100644
--- a/src/interfaces/libpq/fe-auth.h
+++ b/src/interfaces/libpq/fe-auth.h
@@ -23,19 +23,13 @@ extern int	pg_fe_sendauth(AuthRequest areq, int payloadlen, PGconn *conn);
 extern char *pg_fe_getauthname(PQExpBuffer errorMessage);
 
 /* Prototypes for functions in fe-auth-scram.c */
-extern void *pg_fe_scram_init(const char *username,
+extern void *pg_fe_scram_init(PGconn *conn,
 				 const char *password,
-				 bool ssl_in_use,
-				 const char *sasl_mechanism,
-				 const char *channel_binding_type,
-				 char *tls_finished_message,
-				 size_t tls_finished_len,
-				 char *certificate_hash,
-				 size_t certificate_hash_len);
+				 const char *sasl_mechanism);
 extern void pg_fe_scram_free(void *opaq);
 extern void pg_fe_scram_exchange(void *opaq, char *input, int inputlen,
 					 char **output, int *outputlen,
-					 bool *done, bool *success, PQExpBuffer errorMessage);
+					 bool *done, bool *success);
 extern char *pg_fe_scram_build_verifier(const char *password);
 
 #endif							/* FE_AUTH_H */
-- 
2.15.1

Reply via email to