Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 3d1ef3a

Browse files
Support configuring multiple ECDH curves
The ssl_ecdh_curve GUC only accepts a single value, but the TLS handshake can list multiple curves in the groups extension (the extension has been renamed to contain more than elliptic curves). This changes the GUC to accept a colon-separated list of curves. This commit also renames the GUC to ssl_groups to match the new nomenclature for the TLS extension. Original patch by Erica Zhang with additional hacking by me. Author: Erica Zhang <ericazhangy2021@qq.com> Author: Daniel Gustafsson <daniel@yesql.se> Reviewed-by: Jacob Champion <jacob.champion@enterprisedb.com> Reviewed-by: Andres Freund <andres@anarazel.de> Reviewed-by: Peter Eisentraut <peter@eisentraut.org> Reviewed-by: Jelte Fennema-Nio <postgres@jeltef.nl> Discussion: https://postgr.es/m/tencent_063F89FA72CCF2E48A0DF5338841988E9809@qq.com
1 parent 6c66b74 commit 3d1ef3a

File tree

7 files changed

+72
-33
lines changed

7 files changed

+72
-33
lines changed

doc/src/sgml/config.sgml

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1452,20 +1452,20 @@ include_dir 'conf.d'
14521452
</listitem>
14531453
</varlistentry>
14541454

1455-
<varlistentry id="guc-ssl-ecdh-curve" xreflabel="ssl_ecdh_curve">
1456-
<term><varname>ssl_ecdh_curve</varname> (<type>string</type>)
1455+
<varlistentry id="guc-ssl-groups" xreflabel="ssl_groups">
1456+
<term><varname>ssl_groups</varname> (<type>string</type>)
14571457
<indexterm>
1458-
<primary><varname>ssl_ecdh_curve</varname> configuration parameter</primary>
1458+
<primary><varname>ssl_groups</varname> configuration parameter</primary>
14591459
</indexterm>
14601460
</term>
14611461
<listitem>
14621462
<para>
14631463
Specifies the name of the curve to use in <acronym>ECDH</acronym> key
14641464
exchange. It needs to be supported by all clients that connect.
1465+
Multiple curves can be specified by using a colon-separated list.
14651466
It does not need to be the same curve used by the server's Elliptic
1466-
Curve key.
1467-
This parameter can only be set in the <filename>postgresql.conf</filename>
1468-
file or on the server command line.
1467+
Curve key. This parameter can only be set in the
1468+
<filename>postgresql.conf</filename> file or on the server command line.
14691469
The default is <literal>prime256v1</literal>.
14701470
</para>
14711471

@@ -1475,9 +1475,16 @@ include_dir 'conf.d'
14751475
<literal>prime256v1</literal> (NIST P-256),
14761476
<literal>secp384r1</literal> (NIST P-384),
14771477
<literal>secp521r1</literal> (NIST P-521).
1478-
The full list of available curves can be shown with the command
1479-
<command>openssl ecparam -list_curves</command>. Not all of them
1480-
are usable in <acronym>TLS</acronym> though.
1478+
An incomplete list of available groups can be shown with the command
1479+
<command>openssl ecparam -list_curves</command>. Not all of them are
1480+
usable with <acronym>TLS</acronym> though, and many supported group
1481+
names and aliases are omitted.
1482+
</para>
1483+
1484+
<para>
1485+
In <productname>PostgreSQL</productname> versions before 18.0 this
1486+
setting was named <literal>ssl_ecdh_curve</literal> and only accepted
1487+
a single value.
14811488
</para>
14821489
</listitem>
14831490
</varlistentry>

src/backend/libpq/be-secure-openssl.c

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ static int alpn_cb(SSL *ssl,
7676
void *userdata);
7777
static bool initialize_dh(SSL_CTX *context, bool isServerStart);
7878
static bool initialize_ecdh(SSL_CTX *context, bool isServerStart);
79+
static const char *SSLerrmessageExt(unsigned long ecode, const char *replacement);
7980
static const char *SSLerrmessage(unsigned long ecode);
8081

8182
static char *X509_NAME_to_cstring(X509_NAME *name);
@@ -1409,35 +1410,50 @@ static bool
14091410
initialize_ecdh(SSL_CTX *context, bool isServerStart)
14101411
{
14111412
#ifndef OPENSSL_NO_ECDH
1412-
EC_KEY *ecdh;
1413-
int nid;
1414-
1415-
nid = OBJ_sn2nid(SSLECDHCurve);
1416-
if (!nid)
1417-
{
1418-
ereport(isServerStart ? FATAL : LOG,
1419-
(errcode(ERRCODE_CONFIG_FILE_ERROR),
1420-
errmsg("ECDH: unrecognized curve name: %s", SSLECDHCurve)));
1421-
return false;
1422-
}
1423-
1424-
ecdh = EC_KEY_new_by_curve_name(nid);
1425-
if (!ecdh)
1413+
if (SSL_CTX_set1_groups_list(context, SSLECDHCurve) != 1)
14261414
{
1415+
/*
1416+
* OpenSSL 3.3.0 introduced proper error messages for group parsing
1417+
* errors, earlier versions returns "no SSL error reported" which is
1418+
* far from helpful. For older versions, we replace with a better
1419+
* error message. Injecting the error into the OpenSSL error queue
1420+
* need APIs from OpenSSL 3.0.
1421+
*/
14271422
ereport(isServerStart ? FATAL : LOG,
1428-
(errcode(ERRCODE_CONFIG_FILE_ERROR),
1429-
errmsg("ECDH: could not create key")));
1423+
errcode(ERRCODE_CONFIG_FILE_ERROR),
1424+
errmsg("failed to set group names specified in ssl_groups: %s",
1425+
SSLerrmessageExt(ERR_get_error(),
1426+
_("No valid groups found"))),
1427+
errhint("Ensure that each group name is spelled correctly and supported by the installed version of OpenSSL"));
14301428
return false;
14311429
}
1432-
1433-
SSL_CTX_set_options(context, SSL_OP_SINGLE_ECDH_USE);
1434-
SSL_CTX_set_tmp_ecdh(context, ecdh);
1435-
EC_KEY_free(ecdh);
14361430
#endif
14371431

14381432
return true;
14391433
}
14401434

1435+
/*
1436+
* Obtain reason string for passed SSL errcode with replacement
1437+
*
1438+
* The error message supplied in replacement will be used in case the error
1439+
* code from OpenSSL is 0, else the error message from SSLerrmessage() will
1440+
* be returned.
1441+
*
1442+
* Not all versions of OpenSSL place an error on the queue even for failing
1443+
* operations, which will yield "no SSL error reported" by SSLerrmessage. This
1444+
* function can be used to ensure that a proper error message is displayed for
1445+
* versions reporting no error, while using the OpenSSL error via SSLerrmessage
1446+
* for versions where there is one.
1447+
*/
1448+
static const char *
1449+
SSLerrmessageExt(unsigned long ecode, const char *replacement)
1450+
{
1451+
if (ecode == 0)
1452+
return replacement;
1453+
else
1454+
return SSLerrmessage(ecode);
1455+
}
1456+
14411457
/*
14421458
* Obtain reason string for passed SSL errcode
14431459
*

src/backend/utils/misc/guc.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ static const unit_conversion time_unit_conversion_table[] =
190190
static const char *const map_old_guc_names[] = {
191191
"sort_mem", "work_mem",
192192
"vacuum_mem", "maintenance_work_mem",
193+
"ssl_ecdh_curve", "ssl_groups",
193194
NULL
194195
};
195196

src/backend/utils/misc/guc_tables.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4656,9 +4656,9 @@ struct config_string ConfigureNamesString[] =
46564656
},
46574657

46584658
{
4659-
{"ssl_ecdh_curve", PGC_SIGHUP, CONN_AUTH_SSL,
4660-
gettext_noop("Sets the curve to use for ECDH."),
4661-
NULL,
4659+
{"ssl_groups", PGC_SIGHUP, CONN_AUTH_SSL,
4660+
gettext_noop("Sets the group(s) to use for Diffie-Hellman key exchange."),
4661+
gettext_noop("Multiple groups can be specified using colon-separated list."),
46624662
GUC_SUPERUSER_ONLY
46634663
},
46644664
&SSLECDHCurve,

src/backend/utils/misc/postgresql.conf.sample

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@
112112
#ssl_key_file = 'server.key'
113113
#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers
114114
#ssl_prefer_server_ciphers = on
115-
#ssl_ecdh_curve = 'prime256v1'
115+
#ssl_groups = 'prime256v1'
116116
#ssl_min_protocol_version = 'TLSv1.2'
117117
#ssl_max_protocol_version = ''
118118
#ssl_dh_params_file = ''

src/test/ssl/t/001_ssltests.pl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,18 @@ sub switch_server_cert
116116
$result = $node->restart(fail_ok => 1);
117117
is($result, 1, 'restart succeeds with correct SSL protocol bounds');
118118

119+
# Test parsing colon-separated groups. Resetting to a default value to clear
120+
# the error is fine since the call to switch_server_cert in the client side
121+
# tests will overwrite ssl_groups with a known set of groups.
122+
$node->append_conf('sslconfig.conf', qq{ssl_groups='bad:value'});
123+
my $log_size = -s $node->logfile;
124+
$result = $node->restart(fail_ok => 1);
125+
is($result, 0, 'restart fails with incorrect groups');
126+
ok($node->log_contains(qr/no SSL error reported/) == 0,
127+
'error message translated');
128+
$node->append_conf('ssl_config.conf', qq{ssl_groups='prime256v1'});
129+
$result = $node->restart(fail_ok => 1);
130+
119131
### Run client-side tests.
120132
###
121133
### Test that libpq accepts/rejects the connection correctly, depending

src/test/ssl/t/SSL/Server.pm

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,9 @@ sub switch_server_cert
300300
ok(unlink($node->data_dir . '/sslconfig.conf'));
301301
$node->append_conf('sslconfig.conf', "ssl=on");
302302
$node->append_conf('sslconfig.conf', $backend->set_server_cert(\%params));
303+
# use lists of ECDH curves for syntax testing
304+
$node->append_conf('sslconfig.conf', 'ssl_groups=prime256v1:secp521r1');
305+
303306
$node->append_conf('sslconfig.conf',
304307
"ssl_passphrase_command='" . $params{passphrase_cmd} . "'")
305308
if defined $params{passphrase_cmd};

0 commit comments

Comments
 (0)