Add support to the SCRAM exchange for clients that support channel
binding, such as PostgreSQL version 11 and beyond. If such a client
encounters a PgBouncer server that does not support channel binding,
it will send a channel binding flag 'y', meaning the client supports
channel binding but thinks the server does not. But PgBouncer
erroneously did not accept that flag. This would cause connections to
fail if a PostgreSQL version 11 client connects to a PgBouncer with
SCRAM authentication over SSL.
adapted from PostgreSQL
218b024a7ec866ec62abb5c2fb4eb9108bb5fc0f
Reported-by: Andrew Dunstan
char *server_nonce;
char *server_first_message;
uint8_t *SaltedPassword;
+ char cbind_flag;
int iterations;
char *salt; /* base64-encoded */
uint8_t StoredKey[32]; /* SHA256_DIGEST_LENGTH */
*/
bool read_client_first_message(PgSocket *client, char *input,
+ char *cbind_flag_p,
char **client_first_message_bare_p,
char **client_nonce_p);
input = ibuf;
slog_debug(client, "SCRAM client-first-message = \"%s\"", input);
if (!read_client_first_message(client, input,
+ &client->scram_state.cbind_flag,
&client->scram_state.client_first_message_bare,
&client->scram_state.client_nonce))
goto failed;
*/
bool read_client_first_message(PgSocket *client, char *input,
+ char *cbind_flag_p,
char **client_first_message_bare_p,
char **client_nonce_p)
{
char *client_first_message_bare = NULL;
char *client_nonce = NULL;
+ *cbind_flag_p = *input;
switch (*input) {
case 'n':
/* Client does not support channel binding */
/*
* Read channel-binding. We don't support channel binding, so
* it's expected to always be "biws", which is "n,,",
- * base64-encoded.
+ * base64-encoded, or "eSws", which is "y,,". We also have to
+ * check whether the flag is the same one that the client
+ * originally sent.
*/
channel_binding = read_attr_value(client, &input, 'c');
if (channel_binding == NULL)
goto failed;
- if (strcmp(channel_binding, "biws") != 0) {
+ if (!(strcmp(channel_binding, "biws") == 0 && client->scram_state.cbind_flag == 'n') &&
+ !(strcmp(channel_binding, "eSws") == 0 && client->scram_state.cbind_flag == 'y')) {
slog_error(client, "unexpected SCRAM channel-binding attribute in client-final-message");
goto failed;
}
ulimit -c unlimited
+# System configuration checks
+SED_ERE_OP='-E'
+case `uname` in
+Linux)
+ SED_ERE_OP='-r'
+ ;;
+esac
+
+pg_majorversion=$(initdb --version | sed -n $SED_ERE_OP 's/.* ([0-9]+).*/\1/p')
+if test $pg_majorversion -ge 10; then
+ pg_supports_scram=true
+else
+ pg_supports_scram=false
+fi
+
stopit() {
local pid
if test -f "$1"; then
status=$?
if [ $status -eq 0 ]; then
echo "ok"
+ elif [ $status -eq 77 ]; then
+ echo "skipped"
+ status=0
else
echo "FAILED"
cat $LOGDIR/$1.log | sed 's/^/# /'
}
psql_bouncer() {
- PGUSER=bouncer psql -X "$@"
+ PGUSER=bouncer PGPASSWORD=zzz psql -X "$@"
}
# server_lifetime
return $rc
}
+test_client_ssl_scram() {
+ $pg_supports_scram || return 77
+
+ reconf_bouncer "auth_type = scram-sha-256" "server_tls_sslmode = prefer" \
+ "client_tls_sslmode = require" \
+ "client_tls_key_file = TestCA1/sites/01-localhost.key" \
+ "client_tls_cert_file = TestCA1/sites/01-localhost.crt"
+ reconf_pgsql "ssl=on" "ssl_ca_file='root.crt'"
+ psql_bouncer -q -d "dbname=p0 sslmode=verify-full sslrootcert=TestCA1/ca.crt" -c "select 'client-ssl-connect'" | tee tmp/test.tmp 2>&1
+ grep -q "client-ssl-connect" tmp/test.tmp
+ rc=$?
+ return $rc
+}
+
testlist="
test_server_ssl
test_server_ssl_verify
test_server_ssl_pg_auth
test_client_ssl
test_client_ssl_auth
+test_client_ssl_scram
"
if [ $# -gt 0 ]; then
testlist="$*"