From da0c193b1e1476fc994864aa23173964932be0ef Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <dgustafsson@postgresql.org>
Date: Wed, 3 Jul 2024 17:50:25 +0200
Subject: [PATCH v3 2/2] Support TLSv1.3 cipher suites

---
 doc/src/sgml/config.sgml                      | 37 +++++++++++++++----
 src/backend/libpq/be-secure-openssl.c         | 25 +++++++++++--
 src/backend/libpq/be-secure.c                 |  1 +
 src/backend/utils/misc/guc_tables.c           | 15 +++++++-
 src/backend/utils/misc/postgresql.conf.sample |  3 +-
 src/include/libpq/libpq.h                     |  1 +
 src/test/ssl/t/SSL/Server.pm                  |  3 +-
 7 files changed, 70 insertions(+), 15 deletions(-)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 67a5e3ea3d..0ca01580b8 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -1340,6 +1340,29 @@ include_dir 'conf.d'
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-ssl-cipher-suites" xreflabel="ssl_cipher_suites">
+      <term><varname>ssl_cipher_suites</varname> (<type>string</type>)
+      <indexterm>
+       <primary><varname>ssl_cipher_suites</varname> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Specifies a list of <acronym>SSL</acronym> cipher suites that are
+        allowed by connections using TLS version 1.3. Multiple cipher suites
+        can be specified by using a colon separated list. If left blank, the
+        default set of cipher suites in <productname>OpenSSL</productname>
+        will be used.
+       </para>
+
+       <para>
+        This parameter can only be set in the
+        <filename>postgresql.conf</filename> file or on the server command
+        line.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-ssl-ciphers" xreflabel="ssl_ciphers">
       <term><varname>ssl_ciphers</varname> (<type>string</type>)
       <indexterm>
@@ -1348,15 +1371,13 @@ include_dir 'conf.d'
       </term>
       <listitem>
        <para>
-        Specifies a list of <acronym>SSL</acronym> cipher suites that are
-        allowed to be used by SSL connections.  See the
-        <citerefentry><refentrytitle>ciphers</refentrytitle></citerefentry>
+        Specifies a list of <acronym>SSL</acronym> ciphers that are allowed by
+        connections using TLS version 1.2 and lower, see
+        <xref linkend="guc-ssl-cipher-suites"/> for TLS version 1.3 connections. See
+        the <citerefentry><refentrytitle>ciphers</refentrytitle></citerefentry>
         manual page in the <productname>OpenSSL</productname> package for the
-        syntax of this setting and a list of supported values.  Only
-        connections using TLS version 1.2 and lower are affected.  There is
-        currently no setting that controls the cipher choices used by TLS
-        version 1.3 connections.  The default value is
-        <literal>HIGH:MEDIUM:+3DES:!aNULL</literal>.  The default is usually a
+        syntax of this setting and a list of supported values.  The default value
+        is <literal>HIGH:MEDIUM:+3DES:!aNULL</literal>.  The default is usually a
         reasonable choice unless you have specific security requirements.
        </para>
 
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 55faf5df38..0dfd5fa9b8 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -100,6 +100,7 @@ be_tls_init(bool isServerStart)
 	SSL_CTX    *context;
 	int			ssl_ver_min = -1;
 	int			ssl_ver_max = -1;
+	int			status;
 
 	/* This stuff need be done only once. */
 	if (!SSL_initialized)
@@ -288,12 +289,30 @@ be_tls_init(bool isServerStart)
 	if (!initialize_ecdh(context, isServerStart))
 		goto error;
 
-	/* set up the allowed cipher list */
-	if (SSL_CTX_set_cipher_list(context, SSLCipherSuites) != 1)
+	/* set up the allowed cipher list for TLSv1.2 and below */
+	if (SSL_CTX_set_cipher_list(context, SSLCipherLists) != 1)
 	{
 		ereport(isServerStart ? FATAL : LOG,
 				(errcode(ERRCODE_CONFIG_FILE_ERROR),
-				 errmsg("could not set the cipher list (no valid ciphers available)")));
+				 errmsg("could not set the TLSv1.2 cipher list (no valid ciphers available)")));
+		goto error;
+	}
+
+	/*
+	 * Set up the allowed cipher suites for TLSv1.3. If the GUC is an empty
+	 * string we set the allowed suites to the OpenSSL default value.
+	 */
+	if (SSLCipherSuites[0])
+		status = SSL_CTX_set_ciphersuites(context, SSLCipherSuites);
+	else
+		status = SSL_CTX_set_ciphersuites(context, TLS_DEFAULT_CIPHERSUITES);
+
+	/* set up the allowed cipher suites */
+	if (status != 1)
+	{
+		ereport(isServerStart ? FATAL : LOG,
+				(errcode(ERRCODE_CONFIG_FILE_ERROR),
+				 errmsg("could not set the TLSv1.3 cipher suites (no valid ciphers available)")));
 		goto error;
 	}
 
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index 1663f36b6b..aba0498d39 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -48,6 +48,7 @@ bool		ssl_loaded_verify_locations = false;
 
 /* GUC variable controlling SSL cipher list */
 char	   *SSLCipherSuites = NULL;
+char	   *SSLCipherLists = NULL;
 
 /* GUC variable for default ECHD curve. */
 char	   *SSLECDHCurve;
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index adf94381fd..4b702fd9f7 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -4576,12 +4576,23 @@ struct config_string ConfigureNamesString[] =
 	},
 
 	{
-		{"ssl_ciphers", PGC_SIGHUP, CONN_AUTH_SSL,
-			gettext_noop("Sets the list of allowed SSL ciphers."),
+		{"ssl_cipher_suites", PGC_SIGHUP, CONN_AUTH_SSL,
+			gettext_noop("Sets the list of allowed TLSv1.3 cipher suites (leave blank for default)."),
 			NULL,
 			GUC_SUPERUSER_ONLY
 		},
 		&SSLCipherSuites,
+		"",
+		NULL, NULL, NULL
+	},
+
+	{
+		{"ssl_ciphers", PGC_SIGHUP, CONN_AUTH_SSL,
+			gettext_noop("Sets the list of allowed TLSv1.2 (and lower) ciphers."),
+			NULL,
+			GUC_SUPERUSER_ONLY
+		},
+		&SSLCipherLists,
 #ifdef USE_OPENSSL
 		"HIGH:MEDIUM:+3DES:!aNULL",
 #else
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 9ec9f97e92..e8e17c8fc2 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -110,7 +110,8 @@
 #ssl_crl_file = ''
 #ssl_crl_dir = ''
 #ssl_key_file = 'server.key'
-#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL'	# allowed SSL ciphers
+#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL'	# allowed TLSv1.2 ciphers
+#ssl_cipher_suites = ''	# allowed TLSv1.3 cipher suites, blank for default
 #ssl_prefer_server_ciphers = on
 #ssl_ecdh_curve = 'prime256v1'
 #ssl_min_protocol_version = 'TLSv1.2'
diff --git a/src/include/libpq/libpq.h b/src/include/libpq/libpq.h
index 142c98462e..5e0d796972 100644
--- a/src/include/libpq/libpq.h
+++ b/src/include/libpq/libpq.h
@@ -118,6 +118,7 @@ extern ssize_t secure_open_gssapi(Port *port);
 
 /* GUCs */
 extern PGDLLIMPORT char *SSLCipherSuites;
+extern PGDLLIMPORT char *SSLCipherLists;
 extern PGDLLIMPORT char *SSLECDHCurve;
 extern PGDLLIMPORT bool SSLPreferServerCiphers;
 extern PGDLLIMPORT int ssl_min_protocol_version;
diff --git a/src/test/ssl/t/SSL/Server.pm b/src/test/ssl/t/SSL/Server.pm
index abc695c6a3..3dc2a6ab23 100644
--- a/src/test/ssl/t/SSL/Server.pm
+++ b/src/test/ssl/t/SSL/Server.pm
@@ -198,8 +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
+	# use lists of ECDH curves and cipher suites for syntax testing
 	print $conf "ssl_ecdh_curve=prime256v1:secp521r1\n";
+	print $conf "ssl_cipher_suites=TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256\n";
 
 	# enable SSL and set up server key
 	print $conf "include 'sslconfig.conf'\n";
-- 
2.39.3 (Apple Git-146)

