diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 4703309254..4e4645136c 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1222,6 +1222,30 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
+
+ scram_channel_binding
+
+
+ Specifies the channel binding type to use with SCRAM authentication.
+ The list of channel binding types supported by server are listed in
+ . An empty value specifies that
+ the client will not use channel binding. The default value is
+ tls-unique.
+
+
+
+ Channel binding is only supported on SSL connections. If the
+ connection is not using SSL, then this setting is ignored.
+
+
+
+ This parameter is mainly intended for protocol testing. In normal
+ use, there should not be a need to choose a channel binding type other
+ than the default one.
+
+
+
+
sslmode
diff --git a/src/interfaces/libpq/fe-auth-scram.c b/src/interfaces/libpq/fe-auth-scram.c
index 4cad93c24a..b8f7a6b5be 100644
--- a/src/interfaces/libpq/fe-auth-scram.c
+++ b/src/interfaces/libpq/fe-auth-scram.c
@@ -93,6 +93,7 @@ pg_fe_scram_init(const char *username,
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)
{
@@ -112,17 +113,14 @@ pg_fe_scram_init(const char *username,
state->tls_finished_message = tls_finished_message;
state->tls_finished_len = tls_finished_len;
state->sasl_mechanism = strdup(sasl_mechanism);
+ state->channel_binding_type = channel_binding_type;
+
if (!state->sasl_mechanism)
{
free(state);
return NULL;
}
- /*
- * Store channel binding type. Only one type is currently supported.
- */
- state->channel_binding_type = SCRAM_CHANNEL_BINDING_TLS_UNIQUE;
-
/* Normalize the password with SASLprep, if possible */
rc = pg_saslprep(password, &prep_password);
if (rc == SASLPREP_OOM)
@@ -375,6 +373,15 @@ build_client_first_message(fe_scram_state *state, PQExpBuffer errormessage)
Assert(state->ssl_in_use);
appendPQExpBuffer(&buf, "p=%s", state->channel_binding_type);
}
+ else if (state->channel_binding_type == NULL ||
+ strlen(state->channel_binding_type) == 0)
+ {
+ /*
+ * Client has chosen to not show to server that it supports channel
+ * binding.
+ */
+ appendPQExpBuffer(&buf, "n");
+ }
else if (state->ssl_in_use)
{
/*
@@ -493,6 +500,9 @@ build_client_final_message(fe_scram_state *state, PQExpBuffer errormessage)
free(cbind_input);
}
+ else if (state->channel_binding_type == NULL ||
+ strlen(state->channel_binding_type) == 0)
+ appendPQExpBuffer(&buf, "c=biws"); /* base64 of "n,," */
else if (state->ssl_in_use)
appendPQExpBuffer(&buf, "c=eSws"); /* base64 of "y,," */
else
diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c
index 2cfdb7c125..3340a9ad93 100644
--- a/src/interfaces/libpq/fe-auth.c
+++ b/src/interfaces/libpq/fe-auth.c
@@ -528,11 +528,13 @@ pg_SASL_init(PGconn *conn, int payloadlen)
/*
* Select the mechanism to use. Pick SCRAM-SHA-256-PLUS over anything
- * else. Pick SCRAM-SHA-256 if nothing else has already been picked.
- * If we add more mechanisms, a more refined priority mechanism might
- * become necessary.
+ * else if a channel binding type is set. Pick SCRAM-SHA-256 if
+ * nothing else has already been picked. If we add more mechanisms, a
+ * more refined priority mechanism might become necessary.
*/
if (conn->ssl_in_use &&
+ conn->scram_channel_binding &&
+ strlen(conn->scram_channel_binding) > 0 &&
strcmp(mechanism_buf.data, SCRAM_SHA256_PLUS_NAME) == 0)
selected_mechanism = SCRAM_SHA256_PLUS_NAME;
else if (strcmp(mechanism_buf.data, SCRAM_SHA256_NAME) == 0 &&
@@ -591,6 +593,7 @@ pg_SASL_init(PGconn *conn, int payloadlen)
password,
conn->ssl_in_use,
selected_mechanism,
+ conn->scram_channel_binding,
tls_finished,
tls_finished_len);
if (!conn->sasl_state)
diff --git a/src/interfaces/libpq/fe-auth.h b/src/interfaces/libpq/fe-auth.h
index 3e92410eae..db319ac071 100644
--- a/src/interfaces/libpq/fe-auth.h
+++ b/src/interfaces/libpq/fe-auth.h
@@ -27,6 +27,7 @@ extern void *pg_fe_scram_init(const char *username,
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);
extern void pg_fe_scram_free(void *opaq);
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 2c175a2a24..68fb9a124a 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -71,6 +71,7 @@ static int ldapServiceLookup(const char *purl, PQconninfoOption *options,
#endif
#include "common/ip.h"
+#include "common/scram-common.h"
#include "mb/pg_wchar.h"
#include "port/pg_bswap.h"
@@ -122,6 +123,7 @@ static int ldapServiceLookup(const char *purl, PQconninfoOption *options,
#define DefaultOption ""
#define DefaultAuthtype ""
#define DefaultTargetSessionAttrs "any"
+#define DefaultSCRAMChannelBinding SCRAM_CHANNEL_BINDING_TLS_UNIQUE
#ifdef USE_SSL
#define DefaultSSLMode "prefer"
#else
@@ -262,6 +264,11 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
"TCP-Keepalives-Count", "", 10, /* strlen(INT32_MAX) == 10 */
offsetof(struct pg_conn, keepalives_count)},
+ {"scram_channel_binding", NULL, DefaultSCRAMChannelBinding, NULL,
+ "SCRAM-Channel-Binding", "D",
+ 21, /* sizeof("tls-server-end-point") == 21 */
+ offsetof(struct pg_conn, scram_channel_binding)},
+
/*
* ssl options are allowed even without client SSL support because the
* client can still handle SSL modes "disable" and "allow". Other
@@ -3469,6 +3476,8 @@ freePGconn(PGconn *conn)
free(conn->keepalives_interval);
if (conn->keepalives_count)
free(conn->keepalives_count);
+ if (conn->scram_channel_binding)
+ free(conn->scram_channel_binding);
if (conn->sslmode)
free(conn->sslmode);
if (conn->sslcert)
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 8412ee8160..f6c1023f37 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -349,6 +349,7 @@ struct pg_conn
* retransmits */
char *keepalives_count; /* maximum number of TCP keepalive
* retransmits */
+ char *scram_channel_binding; /* SCRAM channel binding type */
char *sslmode; /* SSL mode (require,prefer,allow,disable) */
char *sslcompression; /* SSL compression (0 or 1) */
char *sslkey; /* client key filename */
diff --git a/src/test/ssl/t/002_scram.pl b/src/test/ssl/t/002_scram.pl
index 25f75bd52a..324b4888d4 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 => 1;
+use Test::More tests => 4;
use ServerSetup;
use File::Copy;
@@ -34,5 +34,17 @@ $ENV{PGPASSWORD} = "pass";
$common_connstr =
"user=ssltestuser dbname=trustdb sslmode=require hostaddr=$SERVERHOSTADDR";
+# Default settings
test_connect_ok($common_connstr, '',
"SCRAM authentication with default channel binding");
+
+# Channel binding settings
+test_connect_ok($common_connstr,
+ "scram_channel_binding=tls-unique",
+ "SCRAM authentication with tls-unique as channel binding");
+test_connect_ok($common_connstr,
+ "scram_channel_binding=''",
+ "SCRAM authentication without channel binding");
+test_connect_fails($common_connstr,
+ "scram_channel_binding=not-exists",
+ "SCRAM authentication with invalid channel binding");