]> granicus.if.org Git - postgresql/commitdiff
Add libpq connection parameter "scram_channel_binding"
authorPeter Eisentraut <peter_e@gmx.net>
Mon, 18 Dec 2017 23:05:24 +0000 (18:05 -0500)
committerPeter Eisentraut <peter_e@gmx.net>
Tue, 19 Dec 2017 15:12:36 +0000 (10:12 -0500)
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>

doc/src/sgml/libpq.sgml
src/interfaces/libpq/fe-auth-scram.c
src/interfaces/libpq/fe-auth.c
src/interfaces/libpq/fe-auth.h
src/interfaces/libpq/fe-connect.c
src/interfaces/libpq/libpq-int.h
src/test/ssl/t/002_scram.pl

index 4703309254abe0bf83a32fda8a6cb946ae6e56c6..4e4645136c6aa5ea6852539de53f64fbedbe8cff 100644 (file)
@@ -1222,6 +1222,30 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
       </listitem>
      </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">
       <term><literal>sslmode</literal></term>
       <listitem>
index 4cad93c24adb51f25872bb196adee240f468d15d..b8f7a6b5be9d869c26110963a2dd41424d0c582f 100644 (file)
@@ -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
index 2cfdb7c125c52b65017574fc687346bd17330c8b..3340a9ad933101ac0d56dc90bb7253bdb7f4cde6 100644 (file)
@@ -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)
index 3e92410eaedcbd74bca1a4d93098c0d381df9e1f..db319ac071d426d84de1ad14dbc3b3307e2d272c 100644 (file)
@@ -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);
index 2c175a2a24abd8ac5d5d073f737a47123fdbe21b..68fb9a124aa7de5cfe67a6472f890793f6429666 100644 (file)
@@ -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)
index 8412ee81607d4fdd00805c6432dd1034108d0e2a..f6c1023f37418f667740adbf72df02e224b3e8f2 100644 (file)
@@ -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 */
index 25f75bd52acf4e12d10601518afc39f34c2be556..324b4888d42b7900b8222cedc83666795e1b7787 100644 (file)
@@ -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");