| From: | Mark Atwood <mark(at)reviewcommit(dot)com> |
|---|---|
| To: | pgsql-hackers(at)lists(dot)postgresql(dot)org |
| Subject: | [PATCH v1 3/3] Fetch the channel binding digest explicitly with OpenSSL 3.0 and later |
| Date: | 2026-08-05 00:48:05 |
| Message-ID: | 20260805004805.1174492-4-mark@reviewcommit.com |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-hackers |
be_tls_get_certificate_hash() and pgtls_get_peer_certificate_hash()
computed the tls-server-end-point channel binding hash (RFC 5929) using
the implicit EVP_sha256()/EVP_get_digestbynid() digests.
On OpenSSL 3.0 and newer, select the digest by name and fetch it with
EVP_MD_fetch() so the certificate hash is computed by the active provider.
The fetched EVP_MD is freed on every path, including the error paths: it
is not tracked by a resource owner, and the backend raises errors with
elog(ERROR), which does not return. The implicit path is retained for
older OpenSSL and for LibreSSL.
---
src/backend/libpq/be-secure-openssl.c | 73 +++++++++++++++-----
src/interfaces/libpq/fe-secure-openssl.c | 86 ++++++++++++++++++------
2 files changed, 125 insertions(+), 34 deletions(-)
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 6a99a3d7f9..b50d733493 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -2270,7 +2270,6 @@ 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;
@@ -2299,23 +2298,67 @@ be_tls_get_certificate_hash(Port *port, size_t *len)
* (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)
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+
+ /*
+ * On OpenSSL 3.0 and newer, explicitly fetch the digest by name so that it
+ * is served by the loaded provider. The fetched EVP_MD must be freed, so
+ * do so on every path, including the error paths (elog(ERROR) does not
+ * return and the fetched object is not tracked by a resource owner).
+ */
{
- 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;
+ EVP_MD *algo_type;
+ const char *algo_name;
+
+ switch (algo_nid)
+ {
+ case NID_md5:
+ case NID_sha1:
+ algo_name = "SHA256";
+ break;
+ default:
+ algo_name = OBJ_nid2sn(algo_nid);
+ if (algo_name == NULL)
+ elog(ERROR, "could not determine digest for server certificate signature algorithm");
+ break;
+ }
+
+ algo_type = EVP_MD_fetch(NULL, algo_name, NULL);
+ if (algo_type == NULL)
+ elog(ERROR, "could not load digest \"%s\"", algo_name);
+
+ /* generate and save the certificate hash */
+ if (!X509_digest(server_cert, algo_type, hash, &hash_size))
+ {
+ EVP_MD_free(algo_type);
+ elog(ERROR, "could not generate server certificate hash");
+ }
+
+ EVP_MD_free(algo_type);
}
+#else
+ {
+ const EVP_MD *algo_type = NULL;
- /* generate and save the certificate hash */
- if (!X509_digest(server_cert, algo_type, hash, &hash_size))
- elog(ERROR, "could not generate server certificate hash");
+ 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");
+ }
+#endif
cert_hash = palloc(hash_size);
memcpy(cert_hash, hash, hash_size);
diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
index 3e9b87940b..dc65a87ee7 100644
--- a/src/interfaces/libpq/fe-secure-openssl.c
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -371,7 +371,6 @@ 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;
@@ -406,28 +405,77 @@ pgtls_get_peer_certificate_hash(PGconn *conn, size_t *len)
* (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)
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+
+ /*
+ * On OpenSSL 3.0 and newer, explicitly fetch the digest by name so that it
+ * is served by the loaded provider. The fetched EVP_MD must be freed on
+ * every path, including the error paths.
+ */
{
- case NID_md5:
- case NID_sha1:
- algo_type = EVP_sha256();
- break;
- default:
- algo_type = EVP_get_digestbynid(algo_nid);
- if (algo_type == NULL)
- {
- libpq_append_conn_error(conn, "could not find digest for NID %s",
- OBJ_nid2sn(algo_nid));
- return NULL;
- }
- break;
- }
+ EVP_MD *algo_type;
+ const char *algo_name;
+
+ switch (algo_nid)
+ {
+ case NID_md5:
+ case NID_sha1:
+ algo_name = "SHA256";
+ break;
+ default:
+ algo_name = OBJ_nid2sn(algo_nid);
+ if (algo_name == NULL)
+ {
+ libpq_append_conn_error(conn, "could not determine digest for server certificate signature algorithm");
+ return NULL;
+ }
+ break;
+ }
+
+ algo_type = EVP_MD_fetch(NULL, algo_name, NULL);
+ if (algo_type == NULL)
+ {
+ libpq_append_conn_error(conn, "could not load digest \"%s\"", algo_name);
+ return NULL;
+ }
- if (!X509_digest(peer_cert, algo_type, hash, &hash_size))
+ if (!X509_digest(peer_cert, algo_type, hash, &hash_size))
+ {
+ EVP_MD_free(algo_type);
+ libpq_append_conn_error(conn, "could not generate peer certificate hash");
+ return NULL;
+ }
+
+ EVP_MD_free(algo_type);
+ }
+#else
{
- libpq_append_conn_error(conn, "could not generate peer certificate hash");
- return NULL;
+ const EVP_MD *algo_type;
+
+ 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)
+ {
+ libpq_append_conn_error(conn, "could not find digest for NID %s",
+ OBJ_nid2sn(algo_nid));
+ return NULL;
+ }
+ break;
+ }
+
+ if (!X509_digest(peer_cert, algo_type, hash, &hash_size))
+ {
+ libpq_append_conn_error(conn, "could not generate peer certificate hash");
+ return NULL;
+ }
}
+#endif
/* save result */
cert_hash = malloc(hash_size);
--
2.43.0
| From | Date | Subject | |
|---|---|---|---|
| Previous Message | Chao Li | 2026-08-05 00:46:19 | Re: Add more tab=completion rules for DROP PROPERTY GRAPH |