aboutsummaryrefslogtreecommitdiff
path: root/src/interfaces
diff options
context:
space:
mode:
authorPeter Eisentraut <peter_e@gmx.net>2018-01-04 15:18:39 -0500
committerPeter Eisentraut <peter_e@gmx.net>2018-01-04 15:29:50 -0500
commitd3fb72ea6de58d285e278459bca9d7cdf7f6a38b (patch)
tree27a374b84f98441e85da97a68dd4d144c699f38a /src/interfaces
parent39cfe86195f0b5cbc5fbe8d4e3aa6e2b0e322d0b (diff)
downloadpostgresql-d3fb72ea6de58d285e278459bca9d7cdf7f6a38b.tar.gz
postgresql-d3fb72ea6de58d285e278459bca9d7cdf7f6a38b.zip
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>
Diffstat (limited to 'src/interfaces')
-rw-r--r--src/interfaces/libpq/fe-auth-scram.c15
-rw-r--r--src/interfaces/libpq/fe-secure-openssl.c80
-rw-r--r--src/interfaces/libpq/libpq-int.h1
3 files changed, 96 insertions, 0 deletions
diff --git a/src/interfaces/libpq/fe-auth-scram.c b/src/interfaces/libpq/fe-auth-scram.c
index 06c9cb26141..23bd5fb2b61 100644
--- a/src/interfaces/libpq/fe-auth-scram.c
+++ b/src/interfaces/libpq/fe-auth-scram.c
@@ -446,6 +446,21 @@ build_client_final_message(fe_scram_state *state)
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 */
diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
index 7b7390a1fc6..52390640bf3 100644
--- a/src/interfaces/libpq/fe-secure-openssl.c
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -419,6 +419,86 @@ pgtls_get_finished(PGconn *conn, size_t *len)
return result;
}
+/*
+ * Get the hash of the server certificate, for SCRAM channel binding type
+ * tls-server-end-point.
+ *
+ * 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)
+{
+ X509 *peer_cert;
+ const EVP_MD *algo_type;
+ unsigned char hash[EVP_MAX_MD_SIZE]; /* size for SHA-512 */
+ unsigned int hash_size;
+ int algo_nid;
+ char *cert_hash;
+
+ *len = 0;
+
+ if (!conn->peer)
+ return NULL;
+
+ peer_cert = conn->peer;
+
+ /*
+ * 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 determine server certificate signature algorithm\n"));
+ return NULL;
+ }
+
+ /*
+ * 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)
+ {
+ 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, hash, &hash_size))
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not generate peer certificate hash\n"));
+ return NULL;
+ }
+
+ /* save result */
+ cert_hash = 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 516039eea00..4e354098b39 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