Commit 4bbf110d authored by Peter Eisentraut's avatar Peter Eisentraut

Add libpq connection parameter "scram_channel_binding"

This parameter can be used to enforce the channel binding type used
during a SCRAM authentication.  This can be useful to check code paths
where an invalid channel binding type is used by a client and will be
even more useful to allow testing other channel binding types when they
are added.

The default value is tls-unique, which is what RFC 5802 specifies.
Clients can optionally specify an empty value, which has as effect to
not use channel binding and use SCRAM-SHA-256 as chosen SASL mechanism.

More tests for SCRAM and channel binding are added to the SSL test
suite.

Author: Author: Michael Paquier <michael.paquier@gmail.com>
parent ab9e0e71
...@@ -1222,6 +1222,30 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname ...@@ -1222,6 +1222,30 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry id="libpq-scram-channel-binding" xreflabel="scram_channel_binding">
<term><literal>scram_channel_binding</literal></term>
<listitem>
<para>
Specifies the channel binding type to use with SCRAM authentication.
The list of channel binding types supported by server are listed in
<xref linkend="sasl-authentication"/>. An empty value specifies that
the client will not use channel binding. The default value is
<literal>tls-unique</literal>.
</para>
<para>
Channel binding is only supported on SSL connections. If the
connection is not using SSL, then this setting is ignored.
</para>
<para>
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.
</para>
</listitem>
</varlistentry>
<varlistentry id="libpq-connect-sslmode" xreflabel="sslmode"> <varlistentry id="libpq-connect-sslmode" xreflabel="sslmode">
<term><literal>sslmode</literal></term> <term><literal>sslmode</literal></term>
<listitem> <listitem>
......
...@@ -93,6 +93,7 @@ pg_fe_scram_init(const char *username, ...@@ -93,6 +93,7 @@ pg_fe_scram_init(const char *username,
const char *password, const char *password,
bool ssl_in_use, bool ssl_in_use,
const char *sasl_mechanism, const char *sasl_mechanism,
const char *channel_binding_type,
char *tls_finished_message, char *tls_finished_message,
size_t tls_finished_len) size_t tls_finished_len)
{ {
...@@ -112,17 +113,14 @@ pg_fe_scram_init(const char *username, ...@@ -112,17 +113,14 @@ pg_fe_scram_init(const char *username,
state->tls_finished_message = tls_finished_message; state->tls_finished_message = tls_finished_message;
state->tls_finished_len = tls_finished_len; state->tls_finished_len = tls_finished_len;
state->sasl_mechanism = strdup(sasl_mechanism); state->sasl_mechanism = strdup(sasl_mechanism);
state->channel_binding_type = channel_binding_type;
if (!state->sasl_mechanism) if (!state->sasl_mechanism)
{ {
free(state); free(state);
return NULL; 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 */ /* Normalize the password with SASLprep, if possible */
rc = pg_saslprep(password, &prep_password); rc = pg_saslprep(password, &prep_password);
if (rc == SASLPREP_OOM) if (rc == SASLPREP_OOM)
...@@ -375,6 +373,15 @@ build_client_first_message(fe_scram_state *state, PQExpBuffer errormessage) ...@@ -375,6 +373,15 @@ build_client_first_message(fe_scram_state *state, PQExpBuffer errormessage)
Assert(state->ssl_in_use); Assert(state->ssl_in_use);
appendPQExpBuffer(&buf, "p=%s", state->channel_binding_type); 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) else if (state->ssl_in_use)
{ {
/* /*
...@@ -493,6 +500,9 @@ build_client_final_message(fe_scram_state *state, PQExpBuffer errormessage) ...@@ -493,6 +500,9 @@ build_client_final_message(fe_scram_state *state, PQExpBuffer errormessage)
free(cbind_input); 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) else if (state->ssl_in_use)
appendPQExpBuffer(&buf, "c=eSws"); /* base64 of "y,," */ appendPQExpBuffer(&buf, "c=eSws"); /* base64 of "y,," */
else else
......
...@@ -528,11 +528,13 @@ pg_SASL_init(PGconn *conn, int payloadlen) ...@@ -528,11 +528,13 @@ pg_SASL_init(PGconn *conn, int payloadlen)
/* /*
* Select the mechanism to use. Pick SCRAM-SHA-256-PLUS over anything * Select the mechanism to use. Pick SCRAM-SHA-256-PLUS over anything
* else. Pick SCRAM-SHA-256 if nothing else has already been picked. * else if a channel binding type is set. Pick SCRAM-SHA-256 if
* If we add more mechanisms, a more refined priority mechanism might * nothing else has already been picked. If we add more mechanisms, a
* become necessary. * more refined priority mechanism might become necessary.
*/ */
if (conn->ssl_in_use && if (conn->ssl_in_use &&
conn->scram_channel_binding &&
strlen(conn->scram_channel_binding) > 0 &&
strcmp(mechanism_buf.data, SCRAM_SHA256_PLUS_NAME) == 0) strcmp(mechanism_buf.data, SCRAM_SHA256_PLUS_NAME) == 0)
selected_mechanism = SCRAM_SHA256_PLUS_NAME; selected_mechanism = SCRAM_SHA256_PLUS_NAME;
else if (strcmp(mechanism_buf.data, SCRAM_SHA256_NAME) == 0 && else if (strcmp(mechanism_buf.data, SCRAM_SHA256_NAME) == 0 &&
...@@ -591,6 +593,7 @@ pg_SASL_init(PGconn *conn, int payloadlen) ...@@ -591,6 +593,7 @@ pg_SASL_init(PGconn *conn, int payloadlen)
password, password,
conn->ssl_in_use, conn->ssl_in_use,
selected_mechanism, selected_mechanism,
conn->scram_channel_binding,
tls_finished, tls_finished,
tls_finished_len); tls_finished_len);
if (!conn->sasl_state) if (!conn->sasl_state)
......
...@@ -27,6 +27,7 @@ extern void *pg_fe_scram_init(const char *username, ...@@ -27,6 +27,7 @@ extern void *pg_fe_scram_init(const char *username,
const char *password, const char *password,
bool ssl_in_use, bool ssl_in_use,
const char *sasl_mechanism, const char *sasl_mechanism,
const char *channel_binding_type,
char *tls_finished_message, char *tls_finished_message,
size_t tls_finished_len); size_t tls_finished_len);
extern void pg_fe_scram_free(void *opaq); extern void pg_fe_scram_free(void *opaq);
......
...@@ -71,6 +71,7 @@ static int ldapServiceLookup(const char *purl, PQconninfoOption *options, ...@@ -71,6 +71,7 @@ static int ldapServiceLookup(const char *purl, PQconninfoOption *options,
#endif #endif
#include "common/ip.h" #include "common/ip.h"
#include "common/scram-common.h"
#include "mb/pg_wchar.h" #include "mb/pg_wchar.h"
#include "port/pg_bswap.h" #include "port/pg_bswap.h"
...@@ -122,6 +123,7 @@ static int ldapServiceLookup(const char *purl, PQconninfoOption *options, ...@@ -122,6 +123,7 @@ static int ldapServiceLookup(const char *purl, PQconninfoOption *options,
#define DefaultOption "" #define DefaultOption ""
#define DefaultAuthtype "" #define DefaultAuthtype ""
#define DefaultTargetSessionAttrs "any" #define DefaultTargetSessionAttrs "any"
#define DefaultSCRAMChannelBinding SCRAM_CHANNEL_BINDING_TLS_UNIQUE
#ifdef USE_SSL #ifdef USE_SSL
#define DefaultSSLMode "prefer" #define DefaultSSLMode "prefer"
#else #else
...@@ -262,6 +264,11 @@ static const internalPQconninfoOption PQconninfoOptions[] = { ...@@ -262,6 +264,11 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
"TCP-Keepalives-Count", "", 10, /* strlen(INT32_MAX) == 10 */ "TCP-Keepalives-Count", "", 10, /* strlen(INT32_MAX) == 10 */
offsetof(struct pg_conn, keepalives_count)}, 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 * ssl options are allowed even without client SSL support because the
* client can still handle SSL modes "disable" and "allow". Other * client can still handle SSL modes "disable" and "allow". Other
...@@ -3469,6 +3476,8 @@ freePGconn(PGconn *conn) ...@@ -3469,6 +3476,8 @@ freePGconn(PGconn *conn)
free(conn->keepalives_interval); free(conn->keepalives_interval);
if (conn->keepalives_count) if (conn->keepalives_count)
free(conn->keepalives_count); free(conn->keepalives_count);
if (conn->scram_channel_binding)
free(conn->scram_channel_binding);
if (conn->sslmode) if (conn->sslmode)
free(conn->sslmode); free(conn->sslmode);
if (conn->sslcert) if (conn->sslcert)
......
...@@ -349,6 +349,7 @@ struct pg_conn ...@@ -349,6 +349,7 @@ struct pg_conn
* retransmits */ * retransmits */
char *keepalives_count; /* maximum number of TCP keepalive char *keepalives_count; /* maximum number of TCP keepalive
* retransmits */ * retransmits */
char *scram_channel_binding; /* SCRAM channel binding type */
char *sslmode; /* SSL mode (require,prefer,allow,disable) */ char *sslmode; /* SSL mode (require,prefer,allow,disable) */
char *sslcompression; /* SSL compression (0 or 1) */ char *sslcompression; /* SSL compression (0 or 1) */
char *sslkey; /* client key filename */ char *sslkey; /* client key filename */
......
...@@ -4,7 +4,7 @@ use strict; ...@@ -4,7 +4,7 @@ use strict;
use warnings; use warnings;
use PostgresNode; use PostgresNode;
use TestLib; use TestLib;
use Test::More tests => 1; use Test::More tests => 4;
use ServerSetup; use ServerSetup;
use File::Copy; use File::Copy;
...@@ -34,5 +34,17 @@ $ENV{PGPASSWORD} = "pass"; ...@@ -34,5 +34,17 @@ $ENV{PGPASSWORD} = "pass";
$common_connstr = $common_connstr =
"user=ssltestuser dbname=trustdb sslmode=require hostaddr=$SERVERHOSTADDR"; "user=ssltestuser dbname=trustdb sslmode=require hostaddr=$SERVERHOSTADDR";
# Default settings
test_connect_ok($common_connstr, '', test_connect_ok($common_connstr, '',
"SCRAM authentication with default channel binding"); "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");
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment