From 4510e906e490d934f628cb9d919cf5659542f5a8 Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <dgustafsson@postgresql.org>
Date: Wed, 3 Jul 2024 17:49:12 +0200
Subject: [PATCH v3 1/2] Support multiple ECDH curves

---
 doc/src/sgml/config.sgml              |  1 +
 src/backend/libpq/be-secure-openssl.c | 48 ++++++++++++++++++++-------
 src/backend/utils/misc/guc_tables.c   |  4 +--
 src/test/ssl/t/SSL/Server.pm          |  3 ++
 4 files changed, 42 insertions(+), 14 deletions(-)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 17d84bd321..67a5e3ea3d 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -1462,6 +1462,7 @@ include_dir 'conf.d'
        <para>
         Specifies the name of the curve to use in <acronym>ECDH</acronym> key
         exchange.  It needs to be supported by all clients that connect.
+        Multiple curves can be specified by using a colon-separated list.
         It does not need to be the same curve used by the server's Elliptic
         Curve key.
         This parameter can only be set in the <filename>postgresql.conf</filename>
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 39b1a66236..55faf5df38 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -1402,30 +1402,54 @@ static bool
 initialize_ecdh(SSL_CTX *context, bool isServerStart)
 {
 #ifndef OPENSSL_NO_ECDH
-	EC_KEY	   *ecdh;
-	int			nid;
+	StringInfoData errstr;
+	int			errors = 0;
+	char	   *curve_list = pstrdup(SSLECDHCurve);
+	char	   *saveptr;
+	char	   *token = strtok_r(curve_list, ":", &saveptr);
 
-	nid = OBJ_sn2nid(SSLECDHCurve);
-	if (!nid)
+	initStringInfo(&errstr);
+
+	/*
+	 * Parse the colon separated list and test each member for being a known
+	 * curve name in order be able to provide a good errormessage.  The full
+	 * list is parsed to catch all incorrect names rather than having the user
+	 * fix one only to error out again in the case of multiple errors.
+	 */
+	while (token != NULL)
+	{
+		if (OBJ_sn2nid(token) == NID_undef)
+		{
+			appendStringInfo(&errstr, "%s%s", errors ? ", " : "", token);
+			errors++;
+		}
+
+		token = strtok_r(NULL, ":", &saveptr);
+	}
+
+	if (errors > 0)
 	{
 		ereport(isServerStart ? FATAL : LOG,
 				(errcode(ERRCODE_CONFIG_FILE_ERROR),
-				 errmsg("ECDH: unrecognized curve name: %s", SSLECDHCurve)));
+				 errmsg_plural("ECDH: unrecognized curve name: %s",
+							   "ECDH: unrecognized curve names: %s",
+							   errors, errstr.data)));
+		pfree(errstr.data);
 		return false;
 	}
 
-	ecdh = EC_KEY_new_by_curve_name(nid);
-	if (!ecdh)
+	pfree(errstr.data);
+
+	/*
+	 * No errors were found, apply the entire list.
+	 */
+	if (SSL_CTX_set1_groups_list(context, SSLECDHCurve) != 1)
 	{
 		ereport(isServerStart ? FATAL : LOG,
 				(errcode(ERRCODE_CONFIG_FILE_ERROR),
-				 errmsg("ECDH: could not create key")));
+				 errmsg("ECDH: failed to set curve names")));
 		return false;
 	}
-
-	SSL_CTX_set_options(context, SSL_OP_SINGLE_ECDH_USE);
-	SSL_CTX_set_tmp_ecdh(context, ecdh);
-	EC_KEY_free(ecdh);
 #endif
 
 	return true;
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index d28b0bcb40..adf94381fd 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -4592,8 +4592,8 @@ struct config_string ConfigureNamesString[] =
 
 	{
 		{"ssl_ecdh_curve", PGC_SIGHUP, CONN_AUTH_SSL,
-			gettext_noop("Sets the curve to use for ECDH."),
-			NULL,
+			gettext_noop("Sets the curve(s) to use for ECDH."),
+			gettext_noop("Multiple curves can be specified using colon-separated list."),
 			GUC_SUPERUSER_ONLY
 		},
 		&SSLECDHCurve,
diff --git a/src/test/ssl/t/SSL/Server.pm b/src/test/ssl/t/SSL/Server.pm
index ca4c7b567b..abc695c6a3 100644
--- a/src/test/ssl/t/SSL/Server.pm
+++ b/src/test/ssl/t/SSL/Server.pm
@@ -198,6 +198,9 @@ sub configure_test_server_for_ssl
 	print $conf "listen_addresses='$serverhost'\n";
 	print $conf "log_statement=all\n";
 
+	# use lists of ECDH curves for syntax testing
+	print $conf "ssl_ecdh_curve=prime256v1:secp521r1\n";
+
 	# enable SSL and set up server key
 	print $conf "include 'sslconfig.conf'\n";
 
-- 
2.39.3 (Apple Git-146)

