From 6662750b8e6bc2c9c3fcd5ae727559912a64757d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Renaud=20M=C3=A9trich?= Date: Mon, 15 Jun 2026 08:17:29 +0200 Subject: [PATCH v2 5/8] Add compatibility guards for LibreSSL and older OpenSSL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Guard SSL_CTX_set_current_cert() and SSL_CERT_SET_FIRST/NEXT usage with #ifdef SSL_CERT_SET_FIRST, since these are available in OpenSSL 1.0.2+ but not in LibreSSL. When not available: - ssl_alt_cert_file produces a clear configuration error - ssl_update_ssl() falls back to the original single-cert copy - cert types caching is skipped (ssl_server_cert_types() returns NULL) Author: Renaud Métrich --- src/backend/libpq/be-secure-openssl.c | 36 +++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c index 6964830c5af..2b69be6b46b 100644 --- a/src/backend/libpq/be-secure-openssl.c +++ b/src/backend/libpq/be-secure-openssl.c @@ -558,6 +558,7 @@ be_tls_init(bool isServerStart) * contexts will be freed when PostmasterContext is deleted. */ ssl_cert_types_cached[0] = '\0'; +#ifdef SSL_CERT_SET_FIRST if (new_hosts->default_host && new_hosts->default_host->ssl_ctx) { SSL_CTX *cache_ctx = new_hosts->default_host->ssl_ctx; @@ -593,6 +594,7 @@ be_tls_init(bool isServerStart) ssl_cert_types_cached[cpos] = '\0'; } +#endif /* SSL_CERT_SET_FIRST */ /* * Success! Replace any existing SSL_context and host configurations. @@ -787,7 +789,11 @@ init_host_context(HostsLine *host, bool isServerStart, bool is_default) * the appropriate one during the TLS handshake. Only load for the * default host context (postgresql.conf), not for per-host SNI entries * from pg_hosts.conf. + * + * Requires SSL_CTX_set_current_cert() for same-type detection, which + * is available in OpenSSL 1.0.2+ but not in LibreSSL. */ +#ifdef SSL_CERT_SET_FIRST if (is_default && ssl_alt_cert_file[0] && !ssl_alt_key_file[0]) { ereport(isServerStart ? FATAL : LOG, @@ -868,6 +874,16 @@ init_host_context(HostsLine *host, bool isServerStart, bool is_default) } } } +#else + if (is_default && ssl_alt_cert_file[0]) + { + ereport(isServerStart ? FATAL : LOG, + (errcode(ERRCODE_CONFIG_FILE_ERROR), + errmsg("ssl_alt_cert_file is not supported by this build"), + errhint("Alternate certificate support requires OpenSSL 1.0.2 or later."))); + goto error; + } +#endif /* SSL_CERT_SET_FIRST */ /* * Load CA store, so we can verify client certificates if needed. @@ -1970,7 +1986,11 @@ ssl_update_ssl(SSL *ssl, HostsLine *host_config) * and ECDSA) and copy each onto the per-connection SSL object. * SSL_use_cert_and_key with override=1 replaces; override=0 adds a * cert for a different key type without wiping existing ones. + * + * Fall back to single-cert copy when SSL_CTX_set_current_cert() is + * not available (LibreSSL). */ +#ifdef SSL_CERT_SET_FIRST SSL_CTX_set_current_cert(ctx, SSL_CERT_SET_FIRST); do { @@ -1991,6 +2011,22 @@ ssl_update_ssl(SSL *ssl, HostsLine *host_config) } first = false; } while (SSL_CTX_set_current_cert(ctx, SSL_CERT_SET_NEXT)); +#else + cert = SSL_CTX_get0_certificate(ctx); + key = SSL_CTX_get0_privatekey(ctx); + + Assert(cert && key); + + if (!SSL_CTX_get0_chain_certs(ctx, &chain) + || !SSL_use_cert_and_key(ssl, cert, key, chain, 1)) + { + ereport(COMMERROR, + errcode(ERRCODE_INTERNAL_ERROR), + errmsg_internal("could not update certificate chain: %s", + SSLerrmessage(ERR_get_error()))); + return false; + } +#endif if (!SSL_check_private_key(ssl)) { -- 2.52.0