On Tue, Nov 21, 2017 at 1:36 PM, Michael Paquier <michael.paqu...@gmail.com> wrote: > So attached are rebased patches: > - 0001 to introduce the connection parameter saslchannelbinding, which > allows libpq to enforce the type of channel binding used during an > exchange. > - 0002 to add tls-endpoint as channel binding type, which is where 0001 > shines.
Attached is a rebased patch set, documentation failing to compile. I am moving at the same time this patch set to the next commit fest. -- Michael
From bdd25121ba7c1916d280f97c8e1a280ad26ea60c Mon Sep 17 00:00:00 2001 From: Michael Paquier <mich...@paquier.xyz> Date: Tue, 10 Oct 2017 22:04:22 +0900 Subject: [PATCH 2/2] Implement channel binding tls-server-end-point for SCRAM As referenced in RFC 5929, this channel binding is not the default value and uses a hash of the certificate as binding data. On the frontend, this can be resumed in getting the data from SSL_get_peer_certificate() and on the backend SSL_get_certificate(). The hashing algorithm needs also to switch to SHA-256 if the signature algorithm is MD5 or SHA-1, so let's be careful about that. --- doc/src/sgml/protocol.sgml | 5 +- src/backend/libpq/auth-scram.c | 26 ++++++++--- src/backend/libpq/auth.c | 8 +++- src/backend/libpq/be-secure-openssl.c | 61 +++++++++++++++++++++++++ src/include/libpq/libpq-be.h | 1 + src/include/libpq/scram.h | 4 +- src/interfaces/libpq/fe-auth-scram.c | 24 +++++++--- src/interfaces/libpq/fe-auth.c | 12 ++++- src/interfaces/libpq/fe-auth.h | 4 +- src/interfaces/libpq/fe-secure-openssl.c | 78 ++++++++++++++++++++++++++++++++ src/interfaces/libpq/libpq-int.h | 1 + src/test/ssl/t/002_scram.pl | 5 +- 12 files changed, 210 insertions(+), 19 deletions(-) diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml index 8174e3defa..365f72b51d 100644 --- a/doc/src/sgml/protocol.sgml +++ b/doc/src/sgml/protocol.sgml @@ -1576,8 +1576,9 @@ the password is in. <para> <firstterm>Channel binding</firstterm> is supported in PostgreSQL builds with SSL support. The SASL mechanism name for SCRAM with channel binding -is <literal>SCRAM-SHA-256-PLUS</literal>. The only channel binding type -supported at the moment is <literal>tls-unique</literal>, defined in RFC 5929. +is <literal>SCRAM-SHA-256-PLUS</literal>. Two channel binding types are +supported at the moment: <literal>tls-unique</literal>, which is the default, +and <literal>tls-server-end-point</literal>, both defined in RFC 5929. </para> <procedure> diff --git a/src/backend/libpq/auth-scram.c b/src/backend/libpq/auth-scram.c index 22103ce479..8f96e3927e 100644 --- a/src/backend/libpq/auth-scram.c +++ b/src/backend/libpq/auth-scram.c @@ -113,6 +113,8 @@ typedef struct bool ssl_in_use; const char *tls_finished_message; size_t tls_finished_len; + const char *certificate_hash; + size_t certificate_hash_len; char *channel_binding_type; int iterations; @@ -175,7 +177,9 @@ pg_be_scram_init(const char *username, const char *shadow_pass, bool ssl_in_use, const char *tls_finished_message, - size_t tls_finished_len) + size_t tls_finished_len, + const char *certificate_hash, + size_t certificate_hash_len) { scram_state *state; bool got_verifier; @@ -186,6 +190,8 @@ pg_be_scram_init(const char *username, state->ssl_in_use = ssl_in_use; state->tls_finished_message = tls_finished_message; state->tls_finished_len = tls_finished_len; + state->certificate_hash = certificate_hash; + state->certificate_hash_len = certificate_hash_len; state->channel_binding_type = NULL; /* @@ -852,13 +858,15 @@ read_client_first_message(scram_state *state, char *input) } /* - * Read value provided by client; only tls-unique is supported - * for now. (It is not safe to print the name of an - * unsupported binding type in the error message. Pranksters - * could print arbitrary strings into the log that way.) + * Read value provided by client; only tls-unique and + * tls-server-end-point are supported for now. (It is + * not safe to print the name of an unsupported binding + * type in the error message. Pranksters could print + * arbitrary strings into the log that way.) */ channel_binding_type = read_attr_value(&input, 'p'); - if (strcmp(channel_binding_type, SCRAM_CHANNEL_BINDING_TLS_UNIQUE) != 0) + if (strcmp(channel_binding_type, SCRAM_CHANNEL_BINDING_TLS_UNIQUE) != 0 && + strcmp(channel_binding_type, SCRAM_CHANNEL_BINDING_TLS_ENDPOINT) != 0) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), (errmsg("unsupported SCRAM channel-binding type")))); @@ -1116,6 +1124,12 @@ read_client_final_message(scram_state *state, char *input) cbind_data = state->tls_finished_message; cbind_data_len = state->tls_finished_len; } + else if (strcmp(state->channel_binding_type, + SCRAM_CHANNEL_BINDING_TLS_ENDPOINT) == 0) + { + cbind_data = state->certificate_hash; + cbind_data_len = state->certificate_hash_len; + } else { /* should not happen */ diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c index 2dd3328d71..76fe2645b9 100644 --- a/src/backend/libpq/auth.c +++ b/src/backend/libpq/auth.c @@ -873,6 +873,8 @@ CheckSCRAMAuth(Port *port, char *shadow_pass, char **logdetail) bool initial; char *tls_finished = NULL; size_t tls_finished_len = 0; + char *certificate_hash = NULL; + size_t certificate_hash_len = 0; /* * SASL auth is not supported for protocol versions before 3, because it @@ -920,6 +922,8 @@ CheckSCRAMAuth(Port *port, char *shadow_pass, char **logdetail) if (port->ssl_in_use) { tls_finished = be_tls_get_peer_finished(port, &tls_finished_len); + certificate_hash = be_tls_get_certificate_hash(port, + &certificate_hash_len); } #endif @@ -938,7 +942,9 @@ CheckSCRAMAuth(Port *port, char *shadow_pass, char **logdetail) shadow_pass, port->ssl_in_use, tls_finished, - tls_finished_len); + tls_finished_len, + certificate_hash, + certificate_hash_len); /* * Loop through SASL message exchange. This exchange can consist of diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c index 1e3e19f5e0..e3e8a535c8 100644 --- a/src/backend/libpq/be-secure-openssl.c +++ b/src/backend/libpq/be-secure-openssl.c @@ -1239,6 +1239,67 @@ be_tls_get_peer_finished(Port *port, size_t *len) return result; } +/* + * Get the server certificate hash for authentication purposes. Per + * RFC 5929 and tls-server-end-point, the TLS server's certificate bytes + * need to be hashed with SHA-256 if its signature algorithm is MD5 or + * SHA-1 as per RFC 5929 (https://tools.ietf.org/html/rfc5929#section-4.1). + * If something else is used, the same hash as the signature algorithm is + * used. The result is a palloc'd hash of the server certificate with its + * size, and NULL if there is no certificate available. + */ +char * +be_tls_get_certificate_hash(Port *port, size_t *len) +{ + char *cert_hash = NULL; + X509 *server_cert; + + *len = 0; + server_cert = SSL_get_certificate(port->ssl); + + if (server_cert != NULL) + { + const EVP_MD *algo_type = NULL; + char hash[EVP_MAX_MD_SIZE]; /* size for SHA-512 */ + unsigned int hash_size; + int algo_nid; + + /* + * Get the signature algorithm of the certificate to determine the + * hash algorithm to use for the result. + */ + if (!OBJ_find_sigid_algs(X509_get_signature_nid(server_cert), + &algo_nid, NULL)) + elog(ERROR, "could not find signature algorithm"); + + switch (algo_nid) + { + case NID_md5: + case NID_sha1: + algo_type = EVP_sha256(); + break; + + default: + algo_type = EVP_get_digestbynid(algo_nid); + if (algo_type == NULL) + elog(ERROR, "could not find digest for NID %s", + OBJ_nid2sn(algo_nid)); + break; + } + + /* generate and save the certificate hash */ + if (!X509_digest(server_cert, algo_type, (unsigned char *) hash, + &hash_size)) + elog(ERROR, "could not generate server certificate hash"); + + cert_hash = (char *) palloc(hash_size); + memcpy(cert_hash, hash, hash_size); + *len = hash_size; + } + + return cert_hash; +} + /* * Convert an X509 subject name to a cstring. * diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h index 856e0439d5..cf9d8b7870 100644 --- a/src/include/libpq/libpq-be.h +++ b/src/include/libpq/libpq-be.h @@ -210,6 +210,7 @@ extern void be_tls_get_version(Port *port, char *ptr, size_t len); extern void be_tls_get_cipher(Port *port, char *ptr, size_t len); extern void be_tls_get_peerdn_name(Port *port, char *ptr, size_t len); extern char *be_tls_get_peer_finished(Port *port, size_t *len); +extern char *be_tls_get_certificate_hash(Port *port, size_t *len); #endif extern ProtocolVersion FrontendProtocol; diff --git a/src/include/libpq/scram.h b/src/include/libpq/scram.h index 99560d3d2f..d4e585b3bf 100644 --- a/src/include/libpq/scram.h +++ b/src/include/libpq/scram.h @@ -19,6 +19,7 @@ /* Channel binding types */ #define SCRAM_CHANNEL_BINDING_TLS_UNIQUE "tls-unique" +#define SCRAM_CHANNEL_BINDING_TLS_ENDPOINT "tls-server-end-point" /* Status codes for message exchange */ #define SASL_EXCHANGE_CONTINUE 0 @@ -28,7 +29,8 @@ /* Routines dedicated to authentication */ extern void *pg_be_scram_init(const char *username, const char *shadow_pass, bool ssl_in_use, const char *tls_finished_message, - size_t tls_finished_len); + size_t tls_finished_len, const char *certificate_hash, + size_t certificate_hash_len); extern int pg_be_scram_exchange(void *opaq, char *input, int inputlen, char **output, int *outputlen, char **logdetail); diff --git a/src/interfaces/libpq/fe-auth-scram.c b/src/interfaces/libpq/fe-auth-scram.c index 9860f6b15e..38de1f14cb 100644 --- a/src/interfaces/libpq/fe-auth-scram.c +++ b/src/interfaces/libpq/fe-auth-scram.c @@ -48,6 +48,8 @@ typedef struct bool ssl_in_use; char *tls_finished_message; size_t tls_finished_len; + char *certificate_hash; + size_t certificate_hash_len; char *sasl_mechanism; const char *channel_binding_type; @@ -96,7 +98,9 @@ pg_fe_scram_init(const char *username, const char *sasl_mechanism, const char *channel_binding_type, char *tls_finished_message, - size_t tls_finished_len) + size_t tls_finished_len, + char *certificate_hash, + size_t certificate_hash_len) { fe_scram_state *state; char *prep_password; @@ -113,6 +117,8 @@ pg_fe_scram_init(const char *username, state->ssl_in_use = ssl_in_use; state->tls_finished_message = tls_finished_message; state->tls_finished_len = tls_finished_len; + state->certificate_hash = certificate_hash; + state->certificate_hash_len = certificate_hash_len; state->sasl_mechanism = strdup(sasl_mechanism); if (!state->sasl_mechanism) { @@ -121,9 +127,9 @@ pg_fe_scram_init(const char *username, } /* - * Store channel binding type. Only one type is currently supported, - * tls-unique, which is also the default. Anything defined by the caller - * is forcibly used. + * Store channel binding type. Two types are currently supported, + * tls-unique, which is also the default, and tls-server-end-point. + * Anything defined by the caller is forcibly used. */ if (channel_binding_type && strlen(channel_binding_type) > 0) state->channel_binding_type = channel_binding_type; @@ -165,8 +171,8 @@ pg_fe_scram_free(void *opaq) free(state->password); if (state->tls_finished_message) free(state->tls_finished_message); - if (state->sasl_mechanism) - free(state->sasl_mechanism); + if (state->certificate_hash) + free(state->certificate_hash); /* client messages */ if (state->client_nonce) @@ -457,6 +463,12 @@ build_client_final_message(fe_scram_state *state, PQExpBuffer errormessage) cbind_data = state->tls_finished_message; cbind_data_len = state->tls_finished_len; } + else if (strcmp(state->channel_binding_type, + SCRAM_CHANNEL_BINDING_TLS_ENDPOINT) == 0) + { + cbind_data = state->certificate_hash; + cbind_data_len = state->certificate_hash_len; + } else { /* should not happen */ diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c index 2f34340f45..38cde81d55 100644 --- a/src/interfaces/libpq/fe-auth.c +++ b/src/interfaces/libpq/fe-auth.c @@ -493,6 +493,8 @@ pg_SASL_init(PGconn *conn, int payloadlen) PQExpBufferData mechanism_buf; char *tls_finished = NULL; size_t tls_finished_len = 0; + char *certificate_hash = NULL; + size_t certificate_hash_len = 0; char *password; initPQExpBuffer(&mechanism_buf); @@ -577,6 +579,12 @@ pg_SASL_init(PGconn *conn, int payloadlen) tls_finished = pgtls_get_finished(conn, &tls_finished_len); if (tls_finished == NULL) goto oom_error; + + certificate_hash = + pgtls_get_peer_certificate_hash(conn, + &certificate_hash_len); + if (certificate_hash == NULL) + goto error; /* error message is set */ } #endif @@ -592,7 +600,9 @@ pg_SASL_init(PGconn *conn, int payloadlen) selected_mechanism, conn->saslchannelbinding, tls_finished, - tls_finished_len); + tls_finished_len, + certificate_hash, + certificate_hash_len); if (!conn->sasl_state) goto oom_error; diff --git a/src/interfaces/libpq/fe-auth.h b/src/interfaces/libpq/fe-auth.h index 96094b0c1e..17e85d90f4 100644 --- a/src/interfaces/libpq/fe-auth.h +++ b/src/interfaces/libpq/fe-auth.h @@ -29,7 +29,9 @@ extern void *pg_fe_scram_init(const char *username, const char *sasl_mechanism, const char *channel_binding_type, char *tls_finished_message, - size_t tls_finished_len); + size_t tls_finished_len, + char *certificate_hash, + size_t certificate_hash_len); extern void pg_fe_scram_free(void *opaq); extern void pg_fe_scram_exchange(void *opaq, char *input, int inputlen, char **output, int *outputlen, diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c index 61d161b367..99077c3d9a 100644 --- a/src/interfaces/libpq/fe-secure-openssl.c +++ b/src/interfaces/libpq/fe-secure-openssl.c @@ -419,6 +419,84 @@ pgtls_get_finished(PGconn *conn, size_t *len) return result; } +/* + * Get the hash of the server certificate + * + * This information is useful for end-point channel binding, where the + * client certificate hash is used as a link, per RFC 5929. If the + * signature hash algorithm is MD5 or SHA-1, fall back to SHA-256, + * as per RFC 5929 (https://tools.ietf.org/html/rfc5929#section-4.1). + * NULL is sent back to the caller in the event of an error, with an + * error message for the caller to consume. + */ +char * +pgtls_get_peer_certificate_hash(PGconn *conn, size_t *len) +{ + char *cert_hash = NULL; + + *len = 0; + + if (conn->peer) + { + X509 *peer_cert = conn->peer; + const EVP_MD *algo_type = NULL; + char hash[EVP_MAX_MD_SIZE]; /* size for SHA-512 */ + unsigned int hash_size; + int algo_nid; + + /* + * Get the signature algorithm of the certificate to determine the + * hash algorithm to use for the result. + */ + if (!OBJ_find_sigid_algs(X509_get_signature_nid(peer_cert), + &algo_nid, NULL)) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not find signature algorithm\n")); + return NULL; + } + + switch (algo_nid) + { + case NID_md5: + case NID_sha1: + algo_type = EVP_sha256(); + break; + + default: + algo_type = EVP_get_digestbynid(algo_nid); + if (algo_type == NULL) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not find digest for NID %s\n"), + OBJ_nid2sn(algo_nid)); + return NULL; + } + break; + } + + if (!X509_digest(peer_cert, algo_type, (unsigned char *) hash, + &hash_size)) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("could not generate peer certificate hash\n")); + return NULL; + } + + /* save result */ + cert_hash = (char *) malloc(hash_size); + if (cert_hash == NULL) + { + printfPQExpBuffer(&conn->errorMessage, + libpq_gettext("out of memory\n")); + return NULL; + } + memcpy(cert_hash, hash, hash_size); + *len = hash_size; + } + + return cert_hash; +} /* ------------------------------------------------------------ */ /* OpenSSL specific code */ diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index 1cb096f1ef..df37fc9b7f 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -672,6 +672,7 @@ extern ssize_t pgtls_read(PGconn *conn, void *ptr, size_t len); extern bool pgtls_read_pending(PGconn *conn); extern ssize_t pgtls_write(PGconn *conn, const void *ptr, size_t len); extern char *pgtls_get_finished(PGconn *conn, size_t *len); +extern char *pgtls_get_peer_certificate_hash(PGconn *conn, size_t *len); /* * this is so that we can check if a connection is non-blocking internally diff --git a/src/test/ssl/t/002_scram.pl b/src/test/ssl/t/002_scram.pl index c0b9599920..5a2a24164e 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 => 3; +use Test::More tests => 4; use ServerSetup; use File::Copy; @@ -42,6 +42,9 @@ test_connect_ok($common_connstr, '', test_connect_ok($common_connstr, "saslchannelbinding=tls-unique", "SCRAM authentication with tls-unique as channel binding"); +test_connect_ok($common_connstr, + "saslchannelbinding=tls-server-end-point", + "SCRAM authentication with tls-server-end-point as channel binding"); test_connect_fails($common_connstr, "saslchannelbinding=not-exists", "SCRAM authentication with invalid channel binding"); -- 2.15.0
From b9ee2d9432a0f16cdd59c9789a064d68f0220ad0 Mon Sep 17 00:00:00 2001 From: Michael Paquier <mich...@paquier.xyz> Date: Tue, 21 Nov 2017 12:55:29 +0900 Subject: [PATCH 1/2] Add connection parameter "saslchannelbinding" This parameter can be used to enforce the value of the type of channel binding used during a SASL message exchange. This proves to be useful now to check code paths where an invalid channel binding type is used by a client, which is limited, but will be more useful to allow clients to enforce the channel binding type to tls-enpoint once it gets added. More tests dedicated to SASL and channel binding are added as well to the SSL test suite, which is handy to check the validity of this patch. --- doc/src/sgml/libpq.sgml | 12 ++++++++++++ src/interfaces/libpq/fe-auth-scram.c | 10 ++++++++-- src/interfaces/libpq/fe-auth.c | 1 + src/interfaces/libpq/fe-auth.h | 1 + src/interfaces/libpq/fe-connect.c | 7 +++++++ src/interfaces/libpq/libpq-int.h | 1 + src/test/ssl/t/002_scram.pl | 11 ++++++++++- 7 files changed, 40 insertions(+), 3 deletions(-) diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index 4703309254..2ad2061e9e 100644 --- a/doc/src/sgml/libpq.sgml +++ b/doc/src/sgml/libpq.sgml @@ -1222,6 +1222,18 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname </listitem> </varlistentry> + <varlistentry id="libpq-saslchannelbinding" xreflabel="saslchannelbinding"> + <term><literal>saslchannelbinding</literal></term> + <listitem> + <para> + Controls the name of the channel binding type sent to server when doing + a message exchange for a SASL authentication. The list of channel + binding names supported by server are listed in + <xref linkend="sasl-authentication"/>. + </para> + </listitem> + </varlistentry> + <varlistentry id="libpq-connect-sslmode" xreflabel="sslmode"> <term><literal>sslmode</literal></term> <listitem> diff --git a/src/interfaces/libpq/fe-auth-scram.c b/src/interfaces/libpq/fe-auth-scram.c index f2403147ca..9860f6b15e 100644 --- a/src/interfaces/libpq/fe-auth-scram.c +++ b/src/interfaces/libpq/fe-auth-scram.c @@ -94,6 +94,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) { @@ -120,9 +121,14 @@ pg_fe_scram_init(const char *username, } /* - * Store channel binding type. Only one type is currently supported. + * Store channel binding type. Only one type is currently supported, + * tls-unique, which is also the default. Anything defined by the caller + * is forcibly used. */ - state->channel_binding_type = SCRAM_CHANNEL_BINDING_TLS_UNIQUE; + if (channel_binding_type && strlen(channel_binding_type) > 0) + state->channel_binding_type = channel_binding_type; + else + state->channel_binding_type = SCRAM_CHANNEL_BINDING_TLS_UNIQUE; /* Normalize the password with SASLprep, if possible */ rc = pg_saslprep(password, &prep_password); diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c index 9d394919ef..2f34340f45 100644 --- a/src/interfaces/libpq/fe-auth.c +++ b/src/interfaces/libpq/fe-auth.c @@ -590,6 +590,7 @@ pg_SASL_init(PGconn *conn, int payloadlen) password, conn->ssl_in_use, selected_mechanism, + conn->saslchannelbinding, 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 1525a52742..96094b0c1e 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..3c85501754 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -262,6 +262,11 @@ static const internalPQconninfoOption PQconninfoOptions[] = { "TCP-Keepalives-Count", "", 10, /* strlen(INT32_MAX) == 10 */ offsetof(struct pg_conn, keepalives_count)}, + /* Set of options proper to SASL */ + {"saslchannelbinding", NULL, NULL, NULL, + "SASL-Channel", "", 22, /* sizeof("tls-unique-for-telnet") == 22 */ + offsetof(struct pg_conn, saslchannelbinding)}, + /* * ssl options are allowed even without client SSL support because the * client can still handle SSL modes "disable" and "allow". Other @@ -3469,6 +3474,8 @@ freePGconn(PGconn *conn) free(conn->keepalives_interval); if (conn->keepalives_count) free(conn->keepalives_count); + if (conn->saslchannelbinding) + free(conn->saslchannelbinding); 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..1cb096f1ef 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 *saslchannelbinding; /* channel binding type used in SASL */ 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..c0b9599920 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 => 3; use ServerSetup; use File::Copy; @@ -34,5 +34,14 @@ $ENV{PGPASSWORD} = "pass"; $common_connstr = "user=ssltestuser dbname=trustdb sslmode=require hostaddr=$SERVERHOSTADDR"; +# Defaut settings test_connect_ok($common_connstr, '', "SCRAM authentication with default channel binding"); + +# Channel bindings +test_connect_ok($common_connstr, + "saslchannelbinding=tls-unique", + "SCRAM authentication with tls-unique as channel binding"); +test_connect_fails($common_connstr, + "saslchannelbinding=not-exists", + "SCRAM authentication with invalid channel binding"); -- 2.15.0