I was curious about the hardcoded FILE_DH2048 in src/include/libpq/libpq-be.h -- how did it get there, do we ever need to update it, how do we know, etc. -- and it seems that there is a more modern approach that allows us to get rid of it.

Previously, if ssl_dh_params_file was not set, the compiled-in default
parameters in FILE_DH2048 were used. Instead, we can call SSL_CTX_set_dh_auto() and have OpenSSL choose appropriate, well-known,
and appropriately sized parameters.  In particular, the size of the
parameters is chosen consistent with the size of the key and
certificate, and so it can automatically grow with new standards and
best practices. This facility was not available at the time FILE_DH2048 was added, but it is now available in all relevant OpenSSL and LibreSSL versions.

I think this new behavior is preferable (also consistent with recommendations in the OpenSSL documentation), so I have tweaked the documentation a bit in that direction.

There was also some not-very-modern text about this in README.SSL,
which I have deleted.

Note that if you use a very small certificate/key size, then this
could mean that the size of the chosen DH parameters could also end up
smaller than the previous hard-coded default.  But that could only
happen if you disregarded all current security best practices anyway.

Note a small behavior change: Previously, if loading the DH file
failed during a server reload, the system would then fall back to
using the built-in parameters, instead of failing the reload and
keeping the old SSL context, which is what it would do if some other
aspect of reinitializing SSL would fail. I think this is actually a bug. I have fixed this here as part of reflowing the relevant function.

Anyway, this is all very complicated and obscure.  Reviews are welcome.
From 15bd1bc543f0c851c18576d559071a5c5af24ea0 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Tue, 11 Aug 2026 15:06:37 +0200
Subject: [PATCH] Let OpenSSL auto-select DH parameters by default

Previously, if ssl_dh_params_file was not set, compiled-in default
parameters were used.  This changes this so that in that case, we call
SSL_CTX_set_dh_auto() and have OpenSSL choose appropriate, well-known,
and appropriately sized parameters.  In particular, the size of the
parameters is chosen consistent with the size of the key and
certificate, and so it can automatically grow with new standards and
best practices.  This default behavior is now preferable; it is noted
that setting ssl_dh_params_file is obsolescent.

There was also some not-very-modern text about this in README.SSL,
which is deleted here.

Note that if you use a very small certificate/key size, then this
could mean that the size of the chosen DH parameters could also end up
smaller than the previous hard-coded default.  But that could only
happen if you disregarded all current security best practices anyway.

Note a small behavior change: Previously, if loading the DH file
failed during a server reload, the system would then fall back to
using the built-in parameters, instead of failing the reload and
keeping the old SSL context, which is what it would do if some other
aspect of reinitializing SSL would fail.  This is now fixed.
---
 doc/src/sgml/config.sgml              | 14 +++--
 src/backend/libpq/README.SSL          | 24 --------
 src/backend/libpq/be-secure-openssl.c | 87 +++++++++------------------
 src/include/libpq/libpq-be.h          | 20 ------
 4 files changed, 39 insertions(+), 106 deletions(-)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 10b304122ef..b68edafdd8f 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -1687,11 +1687,15 @@ <title>SSL</title>
        <para>
         Specifies the name of the file containing Diffie-Hellman parameters
         used for so-called ephemeral DH family of SSL ciphers. The default is
-        empty, in which case compiled-in default DH parameters used. Using
-        custom DH parameters reduces the exposure if an attacker manages to
-        crack the well-known compiled-in DH parameters. You can create your own
-        DH parameters file with the command
-        <command>openssl dhparam -out dhparams.pem 2048</command>.
+        empty, in which case the SSL library selects well-known DH parameters
+        that are consistent with the size of the key associated with the
+        server's certificate.  The default approach is preferred; this
+        parameter is obsolescent.
+       </para>
+
+       <para>
+        This mechanism only affects TLS 1.2 and below.  See <xref
+        linkend="guc-ssl-groups"/> for a similar mechanism for TLS 1.3.
        </para>
 
        <para>
diff --git a/src/backend/libpq/README.SSL b/src/backend/libpq/README.SSL
index d84a434a6ee..c07c156d5d9 100644
--- a/src/backend/libpq/README.SSL
+++ b/src/backend/libpq/README.SSL
@@ -56,27 +56,3 @@ SSL
          |                  Yes
          |
    Fail with unknown
-
----------------------------------------------------------------------------
-
-Ephemeral DH
-============
-
-Since the server static private key ($DataDir/server.key) will
-normally be stored unencrypted so that the database backend can
-restart automatically, it is important that we select an algorithm
-that continues to provide confidentiality even if the attacker has the
-server's private key.  Ephemeral DH (EDH) keys provide this and more
-(Perfect Forward Secrecy aka PFS).
-
-N.B., the static private key should still be protected to the largest
-extent possible, to minimize the risk of impersonations.
-
-Another benefit of EDH is that it allows the backend and clients to
-use DSA keys.  DSA keys can only provide digital signatures, not
-encryption, and are often acceptable in jurisdictions where RSA keys
-are unacceptable.
-
-The downside to EDH is that it makes it impossible to use ssldump(1)
-if there's a problem establishing an SSL session.  In this case you'll
-need to temporarily disable EDH (see initialize_dh()).
diff --git a/src/backend/libpq/be-secure-openssl.c 
b/src/backend/libpq/be-secure-openssl.c
index 04764700845..88b85537aec 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -83,7 +83,6 @@ static BIO_METHOD *port_bio_method(void);
 static int     ssl_set_port_bio(Port *port);
 
 static DH  *load_dh_file(char *filename, bool isServerStart);
-static DH  *load_dh_buffer(const char *buffer, size_t len);
 static int     ssl_external_passwd_cb(char *buf, int size, int rwflag, void 
*userdata);
 static int     dummy_ssl_passwd_cb(char *buf, int size, int rwflag, void 
*userdata);
 static int     verify_cb(int ok, X509_STORE_CTX *ctx);
@@ -1538,32 +1537,6 @@ load_dh_file(char *filename, bool isServerStart)
        return dh;
 }
 
-/*
- *     Load hardcoded DH parameters.
- *
- *     If DH parameters cannot be loaded from a specified file, we can load
- *     the hardcoded DH parameters supplied with the backend to prevent
- *     problems.
- */
-static DH  *
-load_dh_buffer(const char *buffer, size_t len)
-{
-       BIO                *bio;
-       DH                 *dh = NULL;
-
-       bio = BIO_new_mem_buf(buffer, len);
-       if (bio == NULL)
-               return NULL;
-       dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
-       if (dh == NULL)
-               ereport(DEBUG2,
-                               (errmsg_internal("DH load buffer: %s",
-                                                                
SSLerrmessage(ERR_get_error()))));
-       BIO_free(bio);
-
-       return dh;
-}
-
 /*
  *     Passphrase collection callback using ssl_passphrase_command
  */
@@ -2072,48 +2045,48 @@ sni_clienthello_cb(SSL *ssl, int *al, void *arg)
 #endif                                                 /* 
HAVE_SSL_CTX_SET_CLIENT_HELLO_CB */
 
 /*
- * Set DH parameters for generating ephemeral DH keys.  The
- * DH parameters can take a long time to compute, so they must be
- * precomputed.
- *
- * Since few sites will bother to create a parameter file, we also
- * provide a fallback to the parameters provided by the OpenSSL
- * project.
- *
- * These values can be static (once loaded or computed) since the
- * OpenSSL library can efficiently generate random keys from the
- * information provided.
+ * Set DH parameters for generating ephemeral DH keys.
  */
 static bool
 initialize_dh(SSL_CTX *context, bool isServerStart)
 {
-       DH                 *dh = NULL;
-
        SSL_CTX_set_options(context, SSL_OP_SINGLE_DH_USE);
 
        if (ssl_dh_params_file[0])
-               dh = load_dh_file(ssl_dh_params_file, isServerStart);
-       if (!dh)
-               dh = load_dh_buffer(FILE_DH2048, sizeof(FILE_DH2048));
-       if (!dh)
        {
-               ereport(isServerStart ? FATAL : LOG,
-                               (errcode(ERRCODE_CONFIG_FILE_ERROR),
-                                errmsg("DH: could not load DH parameters")));
-               return false;
-       }
+               DH                 *dh;
+
+               dh = load_dh_file(ssl_dh_params_file, isServerStart);
+
+               if (!dh)
+                       return false;
+
+               if (SSL_CTX_set_tmp_dh(context, dh) != 1)
+               {
+                       ereport(isServerStart ? FATAL : LOG,
+                                       (errcode(ERRCODE_CONFIG_FILE_ERROR),
+                                        errmsg("DH: could not set DH 
parameters: %s",
+                                                       
SSLerrmessage(ERR_get_error()))));
+                       DH_free(dh);
+                       return false;
+               }
 
-       if (SSL_CTX_set_tmp_dh(context, dh) != 1)
-       {
-               ereport(isServerStart ? FATAL : LOG,
-                               (errcode(ERRCODE_CONFIG_FILE_ERROR),
-                                errmsg("DH: could not set DH parameters: %s",
-                                               
SSLerrmessage(ERR_get_error()))));
                DH_free(dh);
-               return false;
+       }
+       else
+       {
+               /* If ssl_dh_params_file is not set, let OpenSSL pick a 
default. */
+
+               if (SSL_CTX_set_dh_auto(context, 1) != 1)
+               {
+                       ereport(isServerStart ? FATAL : LOG,
+                                       (errcode(ERRCODE_INTERNAL_ERROR),
+                                        errmsg("DH: could not set DH 
parameters: %s",
+                                                       
SSLerrmessage(ERR_get_error()))));
+                       return false;
+               }
        }
 
-       DH_free(dh);
        return true;
 }
 
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..ad51eb7b058 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -252,26 +252,6 @@ typedef struct ClientSocket
 } ClientSocket;
 
 #ifdef USE_SSL
-/*
- *     Hardcoded DH parameters, used in ephemeral DH keying.  (See also
- *     README.SSL for more details on EDH.)
- *
- *     This is the 2048-bit DH parameter from RFC 3526.  The generation of the
- *     prime is specified in RFC 2412 Appendix E, which also discusses the
- *     design choice of the generator.  Note that when loaded with OpenSSL
- *     this causes DH_check() to fail on DH_NOT_SUITABLE_GENERATOR, where
- *     leaking a bit is preferred.
- */
-#define FILE_DH2048 \
-"-----BEGIN DH PARAMETERS-----\n\
-MIIBCAKCAQEA///////////JD9qiIWjCNMTGYouA3BzRKQJOCIpnzHQCC76mOxOb\n\
-IlFKCHmONATd75UZs806QxswKwpt8l8UN0/hNW1tUcJF5IW1dmJefsb0TELppjft\n\
-awv/XLb0Brft7jhr+1qJn6WunyQRfEsf5kkoZlHs5Fs9wgB8uKFjvwWY2kg2HFXT\n\
-mmkWP6j9JM9fg2VdI9yjrZYcYvNWIIVSu57VKQdwlpZtZww1Tkq8mATxdGwIyhgh\n\
-fDKQXkYuNs474553LBgOhgObJ4Oi7Aeij7XFXfBvTFLJ3ivL9pVYFxg5lUl86pVq\n\
-5RXSJhiY+gUQFXKOWoqsqmj//////////wIBAg==\n\
------END DH PARAMETERS-----\n"
-
 /*
  * These functions are implemented by the glue code specific to each
  * SSL implementation (e.g. be-secure-openssl.c)

base-commit: d29d469becec1f2ac082d82863ae1c29fca9dd97
-- 
2.55.0

Reply via email to