From 677485c9fffaa8a425101295cc5843cac4787811 Mon Sep 17 00:00:00 2001
From: Andrey Rachitskiy <pl0h0yp1@gmail.com>
Date: Fri, 30 Oct 2026 23:25:27 +0500
Subject: [PATCH] Fix missing alert when ClientHello has no server_name

sni_clienthello_cb() requests SSL_AD_MISSING_EXTENSION when the
ClientHello has no server_name and pg_hosts.conf has no fallback.
OpenSSL runs the callback before choosing a protocol version and
translates the alert with tls1_alert_code(). OpenSSL 1.1.1 has no
mapping for that alert and closes the socket, so libpq reports
"SSL SYSCALL error: EOF detected". OpenSSL 3 rewrites the same
alert to handshake_failure, which src/test/ssl/t/004_sni.pl already
expects.

Request SSL_AD_HANDSHAKE_FAILURE instead. OpenSSL 1.1.1 sends that
alert, and the client message on OpenSSL 3 stays the same.

Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
Reported-by: Ben Mejia <benjamin.arthur.mejia@gmail.com>
Discussion: https://postgr.es/m/19815-8c6d40e717afe5fd@postgresql.org
Backpatch-through: 19
---
 src/backend/libpq/be-secure-openssl.c | 20 +++++++++-----------
 1 file changed, 9 insertions(+), 11 deletions(-)

diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index fbd4f1173bc..79f3deab30e 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -2076,17 +2076,15 @@ sni_clienthello_cb(SSL *ssl, int *al, void *arg)
 		else
 		{
 			/*
-			 * Reaching here means that we didn't get a hostname in the TLS
-			 * extension and the server has been configured to not allow any
-			 * connections without a specified hostname.
-			 *
-			 * The error message for a missing server_name should, according
-			 * to RFC 8446, be missing_extension. This isn't entirely ideal
-			 * since the user won't be able to tell which extension the server
-			 * considered missing.  Sending unrecognized_name would be a more
-			 * helpful error, but for now we stick to the RFC.
+			 * No server_name extension, and no fallback. RFC 8446 specifies
+			 * missing_extension for this case. This callback runs before
+			 * OpenSSL selects a protocol version, so the alert is translated
+			 * with the TLS 1.2 table. OpenSSL 1.1.1 has no mapping for
+			 * missing_extension and closes the connection without an alert.
+			 * OpenSSL 3 rewrites it to handshake_failure. Send
+			 * handshake_failure so both versions send an alert.
 			 */
-			*al = SSL_AD_MISSING_EXTENSION;
+			*al = SSL_AD_HANDSHAKE_FAILURE;
 
 			ereport(COMMERROR,
 					(errcode(ERRCODE_PROTOCOL_VIOLATION),
@@ -2104,7 +2102,7 @@ sni_clienthello_cb(SSL *ssl, int *al, void *arg)
 		if (tlsext_hostname)
 			*al = SSL_AD_UNRECOGNIZED_NAME;
 		else
-			*al = SSL_AD_MISSING_EXTENSION;
+			*al = SSL_AD_HANDSHAKE_FAILURE;
 		return SSL_CLIENT_HELLO_ERROR;
 	}
 
-- 
2.53.0

