1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-24 00:23:06 +03:00

Implement channel binding tls-server-end-point for SCRAM

This adds a second standard channel binding type for SCRAM.  It is
mainly intended for third-party clients that cannot implement
tls-unique, for example JDBC.

Author: Michael Paquier <michael.paquier@gmail.com>
This commit is contained in:
Peter Eisentraut
2018-01-04 15:18:39 -05:00
parent 39cfe86195
commit d3fb72ea6d
9 changed files with 189 additions and 12 deletions

View File

@@ -849,13 +849,14 @@ 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. (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_END_POINT) != 0)
ereport(ERROR,
(errcode(ERRCODE_PROTOCOL_VIOLATION),
(errmsg("unsupported SCRAM channel-binding type"))));
@@ -1114,6 +1115,15 @@ read_client_final_message(scram_state *state, char *input)
{
#ifdef USE_SSL
cbind_data = be_tls_get_peer_finished(state->port, &cbind_data_len);
#endif
}
else if (strcmp(state->channel_binding_type,
SCRAM_CHANNEL_BINDING_TLS_END_POINT) == 0)
{
/* Fetch hash data of server's SSL certificate */
#ifdef USE_SSL
cbind_data = be_tls_get_certificate_hash(state->port,
&cbind_data_len);
#endif
}
else

View File

@@ -1239,6 +1239,67 @@ be_tls_get_peer_finished(Port *port, size_t *len)
return result;
}
/*
* Get the server certificate hash for SCRAM channel binding type
* tls-server-end-point.
*
* 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)
{
X509 *server_cert;
char *cert_hash;
const EVP_MD *algo_type = NULL;
unsigned char hash[EVP_MAX_MD_SIZE]; /* size for SHA-512 */
unsigned int hash_size;
int algo_nid;
*len = 0;
server_cert = SSL_get_certificate(port->ssl);
if (server_cert == NULL)
return NULL;
/*
* 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 determine server certificate signature algorithm");
/*
* 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.
*/
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, hash, &hash_size))
elog(ERROR, "could not generate server certificate hash");
cert_hash = palloc(hash_size);
memcpy(cert_hash, hash, hash_size);
*len = hash_size;
return cert_hash;
}
/*
* Convert an X509 subject name to a cstring.
*