From 608d5402867cc581ff935cf15d9c25f7eba63703 Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <dgustafsson@postgresql.org>
Date: Thu, 16 Apr 2026 15:20:13 +0200
Subject: [PATCH v2-REL_18] Support OpenSSL 4

OpenSSL 4.0.0 changed some parameters and returnvalues to const, so
we need to update our declarations and subsequently cast away const-
ness from a few callsites to make libpq build without warnings. This
is tested with OpenSSL 1.1.1 through 4.0.0 as well as with LibreSSL.

There is also an errormessage change in OpenSSL 4.0.0 which needs to
be covered by our testharness.

Author: Daniel Gustafsson <daniel@yesql.se>
Discussion: https://postgr.es/m/066B07BB-85FA-487C-BE8C-40F791CFC3C4@yesql.se
---
 contrib/sslinfo/sslinfo.c                | 20 ++++++++++----------
 src/backend/libpq/be-secure-openssl.c    | 14 +++++++-------
 src/interfaces/libpq/fe-secure-openssl.c |  9 +++++----
 src/test/ssl/t/001_ssltests.pl           |  6 +++---
 4 files changed, 25 insertions(+), 24 deletions(-)

diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index da702011193..9191bbce5dc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -24,8 +24,8 @@ PG_MODULE_MAGIC_EXT(
 					.version = PG_VERSION
 );
 
-static Datum X509_NAME_field_to_text(X509_NAME *name, text *fieldName);
-static Datum ASN1_STRING_to_text(ASN1_STRING *str);
+static Datum X509_NAME_field_to_text(const X509_NAME *name, text *fieldName);
+static Datum ASN1_STRING_to_text(const ASN1_STRING *str);
 
 /*
  * Function context for data persisting over repeated calls.
@@ -148,7 +148,7 @@ ssl_client_serial(PG_FUNCTION_ARGS)
  * function.
  */
 static Datum
-ASN1_STRING_to_text(ASN1_STRING *str)
+ASN1_STRING_to_text(const ASN1_STRING *str)
 {
 	BIO		   *membuf;
 	size_t		size;
@@ -194,12 +194,12 @@ ASN1_STRING_to_text(ASN1_STRING *str)
  * part of name
  */
 static Datum
-X509_NAME_field_to_text(X509_NAME *name, text *fieldName)
+X509_NAME_field_to_text(const X509_NAME *name, text *fieldName)
 {
 	char	   *string_fieldname;
 	int			nid,
 				index;
-	ASN1_STRING *data;
+	const ASN1_STRING *data;
 
 	string_fieldname = text_to_cstring(fieldName);
 	nid = OBJ_txt2nid(string_fieldname);
@@ -209,7 +209,7 @@ X509_NAME_field_to_text(X509_NAME *name, text *fieldName)
 				 errmsg("invalid X.509 field name: \"%s\"",
 						string_fieldname)));
 	pfree(string_fieldname);
-	index = X509_NAME_get_index_by_NID(name, nid, -1);
+	index = X509_NAME_get_index_by_NID(unconstify(X509_NAME *, name), nid, -1);
 	if (index < 0)
 		return (Datum) 0;
 	data = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, index));
@@ -421,8 +421,8 @@ ssl_extension_info(PG_FUNCTION_ARGS)
 		HeapTuple	tuple;
 		Datum		result;
 		BIO		   *membuf;
-		X509_EXTENSION *ext;
-		ASN1_OBJECT *obj;
+		const X509_EXTENSION *ext;
+		const ASN1_OBJECT *obj;
 		int			nid;
 		int			len;
 
@@ -435,7 +435,7 @@ ssl_extension_info(PG_FUNCTION_ARGS)
 
 		/* Get the extension from the certificate */
 		ext = X509_get_ext(cert, call_cntr);
-		obj = X509_EXTENSION_get_object(ext);
+		obj = X509_EXTENSION_get_object(unconstify(X509_EXTENSION *, ext));
 
 		/* Get the extension name */
 		nid = OBJ_obj2nid(obj);
@@ -448,7 +448,7 @@ ssl_extension_info(PG_FUNCTION_ARGS)
 		nulls[0] = false;
 
 		/* Get the extension value */
-		if (X509V3_EXT_print(membuf, ext, 0, 0) <= 0)
+		if (X509V3_EXT_print(membuf, unconstify(X509_EXTENSION *, ext), 0, 0) <= 0)
 			ereport(ERROR,
 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 					 errmsg("could not print extension value in certificate at position %d",
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index c8b63ef8249..b67b91a54b2 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -78,7 +78,7 @@ static bool initialize_ecdh(SSL_CTX *context, bool isServerStart);
 static const char *SSLerrmessageExt(unsigned long ecode, const char *replacement);
 static const char *SSLerrmessage(unsigned long ecode);
 
-static char *X509_NAME_to_cstring(X509_NAME *name);
+static char *X509_NAME_to_cstring(const X509_NAME *name);
 
 static SSL_CTX *SSL_context = NULL;
 static bool dummy_ssl_passwd_cb_called = false;
@@ -638,18 +638,18 @@ aloop:
 	if (port->peer != NULL)
 	{
 		int			len;
-		X509_NAME  *x509name = X509_get_subject_name(port->peer);
+		const X509_NAME *x509name = X509_get_subject_name(port->peer);
 		char	   *peer_dn;
 		BIO		   *bio = NULL;
 		BUF_MEM    *bio_buf = NULL;
 
-		len = X509_NAME_get_text_by_NID(x509name, NID_commonName, NULL, 0);
+		len = X509_NAME_get_text_by_NID(unconstify(X509_NAME *, x509name), NID_commonName, NULL, 0);
 		if (len != -1)
 		{
 			char	   *peer_cn;
 
 			peer_cn = MemoryContextAlloc(TopMemoryContext, len + 1);
-			r = X509_NAME_get_text_by_NID(x509name, NID_commonName, peer_cn,
+			r = X509_NAME_get_text_by_NID(unconstify(X509_NAME *, x509name), NID_commonName, peer_cn,
 										  len + 1);
 			peer_cn[len] = '\0';
 			if (r != len)
@@ -1642,14 +1642,14 @@ be_tls_get_certificate_hash(Port *port, size_t *len)
  *
  */
 static char *
-X509_NAME_to_cstring(X509_NAME *name)
+X509_NAME_to_cstring(const X509_NAME *name)
 {
 	BIO		   *membuf = BIO_new(BIO_s_mem());
 	int			i,
 				nid,
 				count = X509_NAME_entry_count(name);
-	X509_NAME_ENTRY *e;
-	ASN1_STRING *v;
+	const X509_NAME_ENTRY *e;
+	const ASN1_STRING *v;
 	const char *field_name;
 	size_t		size;
 	char		nullterm;
diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
index d2045c73ae6..1dd9ba2f506 100644
--- a/src/interfaces/libpq/fe-secure-openssl.c
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -67,7 +67,7 @@
 
 static int	verify_cb(int ok, X509_STORE_CTX *ctx);
 static int	openssl_verify_peer_name_matches_certificate_name(PGconn *conn,
-															  ASN1_STRING *name_entry,
+															  const ASN1_STRING *name_entry,
 															  char **store_name);
 static int	openssl_verify_peer_name_matches_certificate_ip(PGconn *conn,
 															ASN1_OCTET_STRING *addr_entry,
@@ -467,7 +467,8 @@ cert_cb(SSL *ssl, void *arg)
  * into a plain C string.
  */
 static int
-openssl_verify_peer_name_matches_certificate_name(PGconn *conn, ASN1_STRING *name_entry,
+openssl_verify_peer_name_matches_certificate_name(PGconn *conn,
+												  const ASN1_STRING *name_entry,
 												  char **store_name)
 {
 	int			len;
@@ -650,14 +651,14 @@ pgtls_verify_peer_name_matches_certificate_guts(PGconn *conn,
 	 */
 	if (check_cn)
 	{
-		X509_NAME  *subject_name;
+		const X509_NAME *subject_name;
 
 		subject_name = X509_get_subject_name(conn->peer);
 		if (subject_name != NULL)
 		{
 			int			cn_index;
 
-			cn_index = X509_NAME_get_index_by_NID(subject_name,
+			cn_index = X509_NAME_get_index_by_NID(unconstify(X509_NAME *, subject_name),
 												  NID_commonName, -1);
 			if (cn_index >= 0)
 			{
diff --git a/src/test/ssl/t/001_ssltests.pl b/src/test/ssl/t/001_ssltests.pl
index 310d70a4c08..973399b63d0 100644
--- a/src/test/ssl/t/001_ssltests.pl
+++ b/src/test/ssl/t/001_ssltests.pl
@@ -819,7 +819,7 @@ $node->connect_fails(
 	"$common_connstr user=ssltestuser sslcert=ssl/client-revoked.crt "
 	  . sslkey('client-revoked.key'),
 	"certificate authorization fails with revoked client cert",
-	expected_stderr => qr|SSL error: ssl[a-z0-9/]* alert certificate revoked|,
+	expected_stderr => qr!SSL error: (ssl[a-z0-9/]*|tls) alert certificate revoked!,
 	log_like => [
 		qr{Client certificate verification failed at depth 0: certificate revoked},
 		qr{Failed certificate data \(unverified\): subject "/CN=ssltestuser", serial number \d+, issuer "/CN=Test CA for PostgreSQL SSL regression test client certs"},
@@ -921,7 +921,7 @@ $node->connect_fails(
 	"$common_connstr user=ssltestuser sslcert=ssl/client-revoked.crt "
 	  . sslkey('client-revoked.key'),
 	"certificate authorization fails with revoked client cert with server-side CRL directory",
-	expected_stderr => qr|SSL error: ssl[a-z0-9/]* alert certificate revoked|,
+	expected_stderr => qr!SSL error: (ssl[a-z0-9/]*|tls) alert certificate revoked!,
 	log_like => [
 		qr{Client certificate verification failed at depth 0: certificate revoked},
 		qr{Failed certificate data \(unverified\): subject "/CN=ssltestuser", serial number \d+, issuer "/CN=Test CA for PostgreSQL SSL regression test client certs"},
@@ -932,7 +932,7 @@ $node->connect_fails(
 	"$common_connstr user=ssltestuser sslcert=ssl/client-revoked-utf8.crt "
 	  . sslkey('client-revoked-utf8.key'),
 	"certificate authorization fails with revoked UTF-8 client cert with server-side CRL directory",
-	expected_stderr => qr|SSL error: ssl[a-z0-9/]* alert certificate revoked|,
+	expected_stderr => qr!SSL error: (ssl[a-z0-9/]*|tls) alert certificate revoked!,
 	log_like => [
 		qr{Client certificate verification failed at depth 0: certificate revoked},
 		qr{Failed certificate data \(unverified\): subject "/CN=\\xce\\x9f\\xce\\xb4\\xcf\\x85\\xcf\\x83\\xcf\\x83\\xce\\xad\\xce\\xb1\\xcf\\x82", serial number \d+, issuer "/CN=Test CA for PostgreSQL SSL regression test client certs"},
-- 
2.39.3 (Apple Git-146)

