Commit 77291139 authored by Heikki Linnakangas's avatar Heikki Linnakangas

Remove support for tls-unique channel binding.

There are some problems with the tls-unique channel binding type. It's not
supported by all SSL libraries, and strictly speaking it's not defined for
TLS 1.3 at all, even though at least in OpenSSL, the functions used for it
still seem to work with TLS 1.3 connections. And since we had no
mechanism to negotiate what channel binding type to use, there would be
awkward interoperability issues if a server only supported some channel
binding types. tls-server-end-point seems feasible to support with any SSL
library, so let's just stick to that.

This removes the scram_channel_binding libpq option altogether, since there
is now only one supported channel binding type.

This also removes all the channel binding tests from the SSL test suite.
They were really just testing the scram_channel_binding option, which
is now gone. Channel binding is used if both client and server support it,
so it is used in the existing tests. It would be good to have some tests
specifically for channel binding, to make sure it really is used, and the
different combinations of a client and a server that support or doesn't
support it. The current set of settings we have make it hard to write such
tests, but I did test those things manually, by disabling
HAVE_BE_TLS_GET_CERTIFICATE_HASH and/or
HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH.

I also removed the SCRAM_CHANNEL_BINDING_TLS_END_POINT constant. This is a
matter of taste, but IMO it's more readable to just use the
"tls-server-end-point" string.

Refactor the checks on whether the SSL library supports the functions
needed for tls-server-end-point channel binding. Now the server won't
advertise, and the client won't choose, the SCRAM-SHA-256-PLUS variant, if
compiled with an OpenSSL version too old to support it.

In the passing, add some sanity checks to check that the chosen SASL
mechanism, SCRAM-SHA-256 or SCRAM-SHA-256-PLUS, matches whether the SCRAM
exchange used channel binding or not. For example, if the client selects
the non-channel-binding variant SCRAM-SHA-256, but in the SCRAM message
uses channel binding anyway. It's harmless from a security point of view,
I believe, and I'm not sure if there are some other conditions that would
cause the connection to fail, but it seems better to be strict about these
things and check explicitly.

Discussion: https://www.postgresql.org/message-id/ec787074-2305-c6f4-86aa-6902f98485a4%40iki.fi
parent 7a46068f
...@@ -1245,34 +1245,6 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname ...@@ -1245,34 +1245,6 @@ 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. While <acronym>SCRAM</acronym> alone prevents
the replay of transmitted hashed passwords, channel binding also
prevents man-in-the-middle attacks.
</para>
<para>
The list of channel binding types supported by the server are
listed in <xref linkend="sasl-authentication"/>. An empty value
specifies that the client will not use channel binding. If this
parameter is not specified, <literal>tls-unique</literal> is used,
if supported by both server and client.
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-replication" xreflabel="replication"> <varlistentry id="libpq-connect-replication" xreflabel="replication">
<term><literal>replication</literal></term> <term><literal>replication</literal></term>
<listitem> <listitem>
......
...@@ -1576,12 +1576,8 @@ the password is in. ...@@ -1576,12 +1576,8 @@ the password is in.
<para> <para>
<firstterm>Channel binding</firstterm> is supported in PostgreSQL builds with <firstterm>Channel binding</firstterm> is supported in PostgreSQL builds with
SSL support. The SASL mechanism name for SCRAM with channel binding is SSL support. The SASL mechanism name for SCRAM with channel binding is
<literal>SCRAM-SHA-256-PLUS</literal>. Two channel binding types are <literal>SCRAM-SHA-256-PLUS</literal>. The channel binding type used by
supported: <literal>tls-unique</literal> and PostgreSQL is <literal>tls-server-end-point</literal>.
<literal>tls-server-end-point</literal>, both defined in RFC 5929. Clients
should use <literal>tls-unique</literal> if they can support it.
<literal>tls-server-end-point</literal> is intended for third-party clients
that cannot support <literal>tls-unique</literal> for some reason.
</para> </para>
<para> <para>
...@@ -1596,19 +1592,11 @@ that cannot support <literal>tls-unique</literal> for some reason. ...@@ -1596,19 +1592,11 @@ that cannot support <literal>tls-unique</literal> for some reason.
<para> <para>
<acronym>SCRAM</acronym> with channel binding prevents such <acronym>SCRAM</acronym> with channel binding prevents such
man-in-the-middle attacks by mixing a value into the transmitted man-in-the-middle attacks by mixing the signature of the server's
password hash that cannot be retransmitted by a fake server. certificate into the transmitted password hash. While a fake server can
In <acronym>SCRAM</acronym> with <literal>tls-unique</literal> retransmit the real server's certificate, it doesn't have access to the
channel binding, the shared secret negotiated during the SSL session private key matching that certificate, and therefore cannot prove it is
is mixed into the user-supplied password hash. The shared secret the owner, causing SSL connection failure.
is partly chosen by the server, but not directly transmitted, making
it impossible for a fake server to create an SSL connection with the
client that has the same shared secret it has with the real server.
<acronym>SCRAM</acronym> with <literal>tls-server-end-point</literal>
mixes a hash of the server's certificate into the user-supplied password
hash. While a fake server can retransmit the real server's certificate,
it doesn't have access to the private key matching that certificate, and
therefore cannot prove it is the owner, causing SSL connection failure.
</para> </para>
<procedure> <procedure>
......
...@@ -2693,10 +2693,7 @@ same commits as above ...@@ -2693,10 +2693,7 @@ same commits as above
the feature currently does not prevent man-in-the-middle the feature currently does not prevent man-in-the-middle
attacks when using libpq and interfaces built using it. It is attacks when using libpq and interfaces built using it. It is
expected that future versions of libpq and interfaces not built expected that future versions of libpq and interfaces not built
using libpq, e.g. JDBC, will allow this capability. The libpq using libpq, e.g. JDBC, will allow this capability.
options to control the optional channel binding type are <link
linkend="libpq-scram-channel-binding"><option>scram_channel_binding=tls-unique</option></link>
and <option>scram_channel_binding=tls-server-end-point</option>.
</para> </para>
</listitem> </listitem>
......
This diff is collapsed.
...@@ -862,8 +862,7 @@ CheckMD5Auth(Port *port, char *shadow_pass, char **logdetail) ...@@ -862,8 +862,7 @@ CheckMD5Auth(Port *port, char *shadow_pass, char **logdetail)
static int static int
CheckSCRAMAuth(Port *port, char *shadow_pass, char **logdetail) CheckSCRAMAuth(Port *port, char *shadow_pass, char **logdetail)
{ {
char *sasl_mechs; StringInfoData sasl_mechs;
char *p;
int mtype; int mtype;
StringInfoData buf; StringInfoData buf;
void *scram_opaq; void *scram_opaq;
...@@ -889,42 +888,16 @@ CheckSCRAMAuth(Port *port, char *shadow_pass, char **logdetail) ...@@ -889,42 +888,16 @@ CheckSCRAMAuth(Port *port, char *shadow_pass, char **logdetail)
/* /*
* Send the SASL authentication request to user. It includes the list of * Send the SASL authentication request to user. It includes the list of
* authentication mechanisms that are supported. The order of mechanisms * authentication mechanisms that are supported.
* is advertised in decreasing order of importance. So the
* channel-binding variants go first, if they are supported. Channel
* binding is only supported in SSL builds.
*/ */
sasl_mechs = palloc(strlen(SCRAM_SHA_256_PLUS_NAME) + initStringInfo(&sasl_mechs);
strlen(SCRAM_SHA_256_NAME) + 3);
p = sasl_mechs;
if (port->ssl_in_use)
{
strcpy(p, SCRAM_SHA_256_PLUS_NAME);
p += strlen(SCRAM_SHA_256_PLUS_NAME) + 1;
}
strcpy(p, SCRAM_SHA_256_NAME);
p += strlen(SCRAM_SHA_256_NAME) + 1;
pg_be_scram_get_mechanisms(port, &sasl_mechs);
/* Put another '\0' to mark that list is finished. */ /* Put another '\0' to mark that list is finished. */
p[0] = '\0'; appendStringInfoChar(&sasl_mechs, '\0');
sendAuthRequest(port, AUTH_REQ_SASL, sasl_mechs, p - sasl_mechs + 1);
pfree(sasl_mechs);
/* sendAuthRequest(port, AUTH_REQ_SASL, sasl_mechs.data, sasl_mechs.len);
* Initialize the status tracker for message exchanges. pfree(sasl_mechs.data);
*
* If the user doesn't exist, or doesn't have a valid password, or it's
* expired, we still go through the motions of SASL authentication, but
* tell the authentication method that the authentication is "doomed".
* That is, it's going to fail, no matter what.
*
* 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, shadow_pass);
/* /*
* Loop through SASL message exchange. This exchange can consist of * Loop through SASL message exchange. This exchange can consist of
...@@ -973,13 +946,20 @@ CheckSCRAMAuth(Port *port, char *shadow_pass, char **logdetail) ...@@ -973,13 +946,20 @@ CheckSCRAMAuth(Port *port, char *shadow_pass, char **logdetail)
const char *selected_mech; const char *selected_mech;
selected_mech = pq_getmsgrawstring(&buf); selected_mech = pq_getmsgrawstring(&buf);
if (strcmp(selected_mech, SCRAM_SHA_256_NAME) != 0 &&
strcmp(selected_mech, SCRAM_SHA_256_PLUS_NAME) != 0) /*
{ * Initialize the status tracker for message exchanges.
ereport(ERROR, *
(errcode(ERRCODE_PROTOCOL_VIOLATION), * If the user doesn't exist, or doesn't have a valid password, or
errmsg("client selected an invalid SASL authentication mechanism"))); * it's expired, we still go through the motions of SASL
} * authentication, but tell the authentication method that the
* authentication is "doomed". That is, it's going to fail, no
* matter what.
*
* 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, selected_mech, shadow_pass);
inputlen = pq_getmsgint(&buf, 4); inputlen = pq_getmsgint(&buf, 4);
if (inputlen == -1) if (inputlen == -1)
......
...@@ -1105,28 +1105,10 @@ be_tls_get_peerdn_name(Port *port, char *ptr, size_t len) ...@@ -1105,28 +1105,10 @@ be_tls_get_peerdn_name(Port *port, char *ptr, size_t len)
ptr[0] = '\0'; ptr[0] = '\0';
} }
char * #ifdef HAVE_X509_GET_SIGNATURE_NID
be_tls_get_peer_finished(Port *port, size_t *len)
{
char dummy[1];
char *result;
/*
* OpenSSL does not offer an API to directly get the length of the
* expected TLS Finished message, so just do a dummy call to grab this
* information to allow caller to do an allocation with a correct size.
*/
*len = SSL_get_peer_finished(port->ssl, dummy, sizeof(dummy));
result = palloc(*len);
(void) SSL_get_peer_finished(port->ssl, result, *len);
return result;
}
char * char *
be_tls_get_certificate_hash(Port *port, size_t *len) be_tls_get_certificate_hash(Port *port, size_t *len)
{ {
#ifdef HAVE_X509_GET_SIGNATURE_NID
X509 *server_cert; X509 *server_cert;
char *cert_hash; char *cert_hash;
const EVP_MD *algo_type = NULL; const EVP_MD *algo_type = NULL;
...@@ -1176,13 +1158,8 @@ be_tls_get_certificate_hash(Port *port, size_t *len) ...@@ -1176,13 +1158,8 @@ be_tls_get_certificate_hash(Port *port, size_t *len)
*len = hash_size; *len = hash_size;
return cert_hash; return cert_hash;
#else
ereport(ERROR,
(errcode(ERRCODE_PROTOCOL_VIOLATION),
errmsg("channel binding type \"tls-server-end-point\" is not supported by this build")));
return NULL;
#endif
} }
#endif
/* /*
* Convert an X509 subject name to a cstring. * Convert an X509 subject name to a cstring.
......
...@@ -19,10 +19,6 @@ ...@@ -19,10 +19,6 @@
#define SCRAM_SHA_256_NAME "SCRAM-SHA-256" #define SCRAM_SHA_256_NAME "SCRAM-SHA-256"
#define SCRAM_SHA_256_PLUS_NAME "SCRAM-SHA-256-PLUS" /* with channel binding */ #define SCRAM_SHA_256_PLUS_NAME "SCRAM-SHA-256-PLUS" /* with channel binding */
/* Channel binding types */
#define SCRAM_CHANNEL_BINDING_TLS_UNIQUE "tls-unique"
#define SCRAM_CHANNEL_BINDING_TLS_END_POINT "tls-server-end-point"
/* Length of SCRAM keys (client and server) */ /* Length of SCRAM keys (client and server) */
#define SCRAM_KEY_LEN PG_SHA256_DIGEST_LENGTH #define SCRAM_KEY_LEN PG_SHA256_DIGEST_LENGTH
......
...@@ -260,24 +260,23 @@ extern const char *be_tls_get_version(Port *port); ...@@ -260,24 +260,23 @@ extern const char *be_tls_get_version(Port *port);
extern const char *be_tls_get_cipher(Port *port); extern const char *be_tls_get_cipher(Port *port);
extern void be_tls_get_peerdn_name(Port *port, char *ptr, size_t len); extern void be_tls_get_peerdn_name(Port *port, char *ptr, size_t len);
/*
* Get the expected TLS Finished message information from the client, useful
* for authorization when doing channel binding.
*
* Result is a palloc'd copy of the TLS Finished message with its size.
*/
extern char *be_tls_get_peer_finished(Port *port, size_t *len);
/* /*
* Get the server certificate hash for SCRAM channel binding type * Get the server certificate hash for SCRAM channel binding type
* tls-server-end-point. * tls-server-end-point.
* *
* The result is a palloc'd hash of the server certificate with its * The result is a palloc'd hash of the server certificate with its
* size, and NULL if there is no certificate available. * size, and NULL if there is no certificate available.
*
* This is not supported with old versions of OpenSSL that don't have
* the X509_get_signature_nid() function.
*/ */
#if defined(USE_OPENSSL) && defined(HAVE_X509_GET_SIGNATURE_NID)
#define HAVE_BE_TLS_GET_CERTIFICATE_HASH
extern char *be_tls_get_certificate_hash(Port *port, size_t *len); extern char *be_tls_get_certificate_hash(Port *port, size_t *len);
#endif #endif
#endif /* USE_SSL */
extern ProtocolVersion FrontendProtocol; extern ProtocolVersion FrontendProtocol;
/* TCP keepalives configuration. These are no-ops on an AF_UNIX socket. */ /* TCP keepalives configuration. These are no-ops on an AF_UNIX socket. */
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#ifndef PG_SCRAM_H #ifndef PG_SCRAM_H
#define PG_SCRAM_H #define PG_SCRAM_H
#include "lib/stringinfo.h"
#include "libpq/libpq-be.h" #include "libpq/libpq-be.h"
/* Status codes for message exchange */ /* Status codes for message exchange */
...@@ -21,7 +22,8 @@ ...@@ -21,7 +22,8 @@
#define SASL_EXCHANGE_FAILURE 2 #define SASL_EXCHANGE_FAILURE 2
/* Routines dedicated to authentication */ /* Routines dedicated to authentication */
extern void *pg_be_scram_init(Port *port, const char *shadow_pass); extern void pg_be_scram_get_mechanisms(Port *port, StringInfo buf);
extern void *pg_be_scram_init(Port *port, const char *selected_mech, const char *shadow_pass);
extern int pg_be_scram_exchange(void *opaq, char *input, int inputlen, extern int pg_be_scram_exchange(void *opaq, char *input, int inputlen,
char **output, int *outputlen, char **logdetail); char **output, int *outputlen, char **logdetail);
......
...@@ -352,17 +352,9 @@ build_client_first_message(fe_scram_state *state) ...@@ -352,17 +352,9 @@ build_client_first_message(fe_scram_state *state)
if (strcmp(state->sasl_mechanism, SCRAM_SHA_256_PLUS_NAME) == 0) if (strcmp(state->sasl_mechanism, SCRAM_SHA_256_PLUS_NAME) == 0)
{ {
Assert(conn->ssl_in_use); Assert(conn->ssl_in_use);
appendPQExpBuffer(&buf, "p=%s", conn->scram_channel_binding); appendPQExpBuffer(&buf, "p=tls-server-end-point");
}
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
* binding.
*/
appendPQExpBuffer(&buf, "n");
} }
#ifdef HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH
else if (conn->ssl_in_use) else if (conn->ssl_in_use)
{ {
/* /*
...@@ -370,6 +362,7 @@ build_client_first_message(fe_scram_state *state) ...@@ -370,6 +362,7 @@ build_client_first_message(fe_scram_state *state)
*/ */
appendPQExpBuffer(&buf, "y"); appendPQExpBuffer(&buf, "y");
} }
#endif
else else
{ {
/* /*
...@@ -432,60 +425,28 @@ build_client_final_message(fe_scram_state *state) ...@@ -432,60 +425,28 @@ build_client_final_message(fe_scram_state *state)
*/ */
if (strcmp(state->sasl_mechanism, SCRAM_SHA_256_PLUS_NAME) == 0) if (strcmp(state->sasl_mechanism, SCRAM_SHA_256_PLUS_NAME) == 0)
{ {
#ifdef HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH
char *cbind_data = NULL; char *cbind_data = NULL;
size_t cbind_data_len = 0; size_t cbind_data_len = 0;
size_t cbind_header_len; size_t cbind_header_len;
char *cbind_input; char *cbind_input;
size_t cbind_input_len; size_t cbind_input_len;
if (strcmp(conn->scram_channel_binding, SCRAM_CHANNEL_BINDING_TLS_UNIQUE) == 0) /* Fetch hash data of server's SSL certificate */
{ cbind_data =
#ifdef USE_SSL pgtls_get_peer_certificate_hash(state->conn,
cbind_data = pgtls_get_finished(state->conn, &cbind_data_len); &cbind_data_len);
if (cbind_data == NULL) if (cbind_data == NULL)
goto oom_error;
#endif
}
else if (strcmp(conn->scram_channel_binding,
SCRAM_CHANNEL_BINDING_TLS_END_POINT) == 0)
{
/* 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(&conn->errorMessage,
libpq_gettext("invalid channel binding type\n"));
return NULL;
}
/* should not happen */
if (cbind_data == NULL || cbind_data_len == 0)
{ {
if (cbind_data != NULL) /* error message is already set on error */
free(cbind_data);
termPQExpBuffer(&buf); termPQExpBuffer(&buf);
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("empty channel binding data for channel binding type \"%s\"\n"),
conn->scram_channel_binding);
return NULL; return NULL;
} }
appendPQExpBuffer(&buf, "c="); appendPQExpBuffer(&buf, "c=");
/* p=type,, */ /* p=type,, */
cbind_header_len = 4 + strlen(conn->scram_channel_binding); cbind_header_len = strlen("p=tls-server-end-point,,");
cbind_input_len = cbind_header_len + cbind_data_len; cbind_input_len = cbind_header_len + cbind_data_len;
cbind_input = malloc(cbind_input_len); cbind_input = malloc(cbind_input_len);
if (!cbind_input) if (!cbind_input)
...@@ -493,8 +454,7 @@ build_client_final_message(fe_scram_state *state) ...@@ -493,8 +454,7 @@ build_client_final_message(fe_scram_state *state)
free(cbind_data); free(cbind_data);
goto oom_error; goto oom_error;
} }
snprintf(cbind_input, cbind_input_len, "p=%s,,", memcpy(cbind_input, "p=tls-server-end-point,,", cbind_header_len);
conn->scram_channel_binding);
memcpy(cbind_input + cbind_header_len, cbind_data, cbind_data_len); memcpy(cbind_input + cbind_header_len, cbind_data, cbind_data_len);
if (!enlargePQExpBuffer(&buf, pg_b64_enc_len(cbind_input_len))) if (!enlargePQExpBuffer(&buf, pg_b64_enc_len(cbind_input_len)))
...@@ -508,12 +468,21 @@ build_client_final_message(fe_scram_state *state) ...@@ -508,12 +468,21 @@ build_client_final_message(fe_scram_state *state)
free(cbind_data); free(cbind_data);
free(cbind_input); free(cbind_input);
#else
/*
* Chose channel binding, but the SSL library doesn't support it.
* Shouldn't happen.
*/
termPQExpBuffer(&buf);
printfPQExpBuffer(&conn->errorMessage,
"channel binding not supported by this build\n");
return NULL;
#endif /* HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH */
} }
else if (conn->scram_channel_binding == NULL || #ifdef HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH
strlen(conn->scram_channel_binding) == 0)
appendPQExpBuffer(&buf, "c=biws"); /* base64 of "n,," */
else if (conn->ssl_in_use) else if (conn->ssl_in_use)
appendPQExpBuffer(&buf, "c=eSws"); /* base64 of "y,," */ appendPQExpBuffer(&buf, "c=eSws"); /* base64 of "y,," */
#endif
else else
appendPQExpBuffer(&buf, "c=biws"); /* base64 of "n,," */ appendPQExpBuffer(&buf, "c=biws"); /* base64 of "n,," */
......
...@@ -530,11 +530,26 @@ pg_SASL_init(PGconn *conn, int payloadlen) ...@@ -530,11 +530,26 @@ pg_SASL_init(PGconn *conn, int payloadlen)
* nothing else has already been picked. If we add more mechanisms, a * nothing else has already been picked. If we add more mechanisms, a
* more refined priority mechanism might become necessary. * more refined priority mechanism might become necessary.
*/ */
if (conn->ssl_in_use && if (strcmp(mechanism_buf.data, SCRAM_SHA_256_PLUS_NAME) == 0)
conn->scram_channel_binding && {
strlen(conn->scram_channel_binding) > 0 && if (conn->ssl_in_use)
strcmp(mechanism_buf.data, SCRAM_SHA_256_PLUS_NAME) == 0) selected_mechanism = SCRAM_SHA_256_PLUS_NAME;
selected_mechanism = SCRAM_SHA_256_PLUS_NAME; else
{
/*
* The server offered SCRAM-SHA-256-PLUS, but the connection
* is not SSL-encrypted. That's not sane. Perhaps SSL was
* stripped by a proxy? There's no point in continuing,
* because the server will reject the connection anyway if we
* try authenticate without channel binding even though both
* the client and server supported it. The SCRAM exchange
* checks for that, to prevent downgrade attacks.
*/
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("server offered SCRAM-SHA-256-PLUS authentication over a non-SSL connection\n"));
goto error;
}
}
else if (strcmp(mechanism_buf.data, SCRAM_SHA_256_NAME) == 0 && else if (strcmp(mechanism_buf.data, SCRAM_SHA_256_NAME) == 0 &&
!selected_mechanism) !selected_mechanism)
selected_mechanism = SCRAM_SHA_256_NAME; selected_mechanism = SCRAM_SHA_256_NAME;
......
...@@ -123,7 +123,6 @@ static int ldapServiceLookup(const char *purl, PQconninfoOption *options, ...@@ -123,7 +123,6 @@ 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
...@@ -264,11 +263,6 @@ static const internalPQconninfoOption PQconninfoOptions[] = { ...@@ -264,11 +263,6 @@ 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
...@@ -3481,8 +3475,6 @@ freePGconn(PGconn *conn) ...@@ -3481,8 +3475,6 @@ 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)
......
...@@ -369,30 +369,10 @@ pgtls_write(PGconn *conn, const void *ptr, size_t len) ...@@ -369,30 +369,10 @@ pgtls_write(PGconn *conn, const void *ptr, size_t len)
return n; return n;
} }
char * #ifdef HAVE_X509_GET_SIGNATURE_NID
pgtls_get_finished(PGconn *conn, size_t *len)
{
char dummy[1];
char *result;
/*
* OpenSSL does not offer an API to get directly the length of the TLS
* Finished message sent, so first do a dummy call to grab this
* information and then do an allocation with the correct size.
*/
*len = SSL_get_finished(conn->ssl, dummy, sizeof(dummy));
result = malloc(*len);
if (result == NULL)
return NULL;
(void) SSL_get_finished(conn->ssl, result, *len);
return result;
}
char * char *
pgtls_get_peer_certificate_hash(PGconn *conn, size_t *len) pgtls_get_peer_certificate_hash(PGconn *conn, size_t *len)
{ {
#ifdef HAVE_X509_GET_SIGNATURE_NID
X509 *peer_cert; X509 *peer_cert;
const EVP_MD *algo_type; const EVP_MD *algo_type;
unsigned char hash[EVP_MAX_MD_SIZE]; /* size for SHA-512 */ unsigned char hash[EVP_MAX_MD_SIZE]; /* size for SHA-512 */
...@@ -462,12 +442,8 @@ pgtls_get_peer_certificate_hash(PGconn *conn, size_t *len) ...@@ -462,12 +442,8 @@ pgtls_get_peer_certificate_hash(PGconn *conn, size_t *len)
*len = hash_size; *len = hash_size;
return cert_hash; return cert_hash;
#else
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("channel binding type \"tls-server-end-point\" is not supported by this build\n"));
return NULL;
#endif
} }
#endif /* HAVE_X509_GET_SIGNATURE_NID */
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
/* OpenSSL specific code */ /* OpenSSL specific code */
......
...@@ -349,7 +349,6 @@ struct pg_conn ...@@ -349,7 +349,6 @@ 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 */
...@@ -715,22 +714,20 @@ extern bool pgtls_read_pending(PGconn *conn); ...@@ -715,22 +714,20 @@ extern bool pgtls_read_pending(PGconn *conn);
*/ */
extern ssize_t pgtls_write(PGconn *conn, const void *ptr, size_t len); extern ssize_t pgtls_write(PGconn *conn, const void *ptr, size_t len);
/*
* Get the TLS finish message sent during last handshake.
*
* This information is useful for callers doing channel binding during
* authentication.
*/
extern char *pgtls_get_finished(PGconn *conn, size_t *len);
/* /*
* Get the hash of the server certificate, for SCRAM channel binding type * Get the hash of the server certificate, for SCRAM channel binding type
* tls-server-end-point. * tls-server-end-point.
* *
* NULL is sent back to the caller in the event of an error, with an * NULL is sent back to the caller in the event of an error, with an
* error message for the caller to consume. * error message for the caller to consume.
*
* This is not supported with old versions of OpenSSL that don't have
* the X509_get_signature_nid() function.
*/ */
#if defined(USE_OPENSSL) && defined(HAVE_X509_GET_SIGNATURE_NID)
#define HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH
extern char *pgtls_get_peer_certificate_hash(PGconn *conn, size_t *len); extern char *pgtls_get_peer_certificate_hash(PGconn *conn, size_t *len);
#endif
/* /*
* Verify that the server certificate matches the host name we connected to. * Verify that the server certificate matches the host name we connected to.
......
...@@ -13,7 +13,7 @@ if ($ENV{with_openssl} ne 'yes') ...@@ -13,7 +13,7 @@ if ($ENV{with_openssl} ne 'yes')
plan skip_all => 'SSL not supported by this build'; plan skip_all => 'SSL not supported by this build';
} }
my $number_of_tests = 6; my $number_of_tests = 1;
# This is the hostname used to connect to the server. # This is the hostname used to connect to the server.
my $SERVERHOSTADDR = '127.0.0.1'; my $SERVERHOSTADDR = '127.0.0.1';
...@@ -47,35 +47,6 @@ $common_connstr = ...@@ -47,35 +47,6 @@ $common_connstr =
# Default settings # Default settings
test_connect_ok($common_connstr, '', test_connect_ok($common_connstr, '',
"SCRAM authentication with default channel binding"); "Basic SCRAM authentication with SSL");
# 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");
if ($supports_tls_server_end_point)
{
test_connect_ok(
$common_connstr,
"scram_channel_binding=tls-server-end-point",
"SCRAM authentication with tls-server-end-point as channel binding");
}
else
{
test_connect_fails(
$common_connstr,
"scram_channel_binding=tls-server-end-point",
qr/channel binding type "tls-server-end-point" is not supported by this build/,
"SCRAM authentication with tls-server-end-point as channel binding");
$number_of_tests++;
}
test_connect_fails(
$common_connstr,
"scram_channel_binding=not-exists",
qr/unsupported SCRAM channel-binding type/,
"SCRAM authentication with invalid channel binding");
done_testing($number_of_tests); done_testing($number_of_tests);
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