Add settings to control SSL/TLS protocol version

For example:

    ssl_min_protocol_version = 'TLSv1.1'
    ssl_max_protocol_version = 'TLSv1.2'

Reviewed-by: Steve Singer <steve@ssinger.info>
Discussion: https://www.postgresql.org/message-id/flat/1822da87-b862-041a-9fc2-d0310c3da173@2ndquadrant.com
This commit is contained in:
Peter Eisentraut 2018-11-20 21:49:01 +01:00
parent 2d9140ed26
commit e73e67c719
6 changed files with 214 additions and 2 deletions

View File

@ -1291,6 +1291,50 @@ include_dir 'conf.d'
</listitem>
</varlistentry>
<varlistentry id="guc-ssl-min-protocol-version" xreflabel="ssl_min_protocol_version">
<term><varname>ssl_min_protocol_version</varname> (<type>enum</type>)
<indexterm>
<primary><varname>ssl_min_protocol_version</varname> configuration parameter</primary>
</indexterm>
</term>
<listitem>
<para>
Sets the minimum SSL/TLS protocol version to use. Valid values are
currently: <literal>TLSv1</literal>, <literal>TLSv1.1</literal>,
<literal>TLSv1.2</literal>, <literal>TLSv1.3</literal>. Older
versions of the <productname>OpenSSL</productname> library do not
support all values; an error will be raised if an unsupported setting
is chosen. Protocol versions before TLS 1.0, namely SSL version 2 and
3, are always disabled.
</para>
<para>
The default is <literal>TLSv1</literal>, mainly to support older
versions of the <productname>OpenSSL</productname> library. You might
want to set this to a higher value if all software components can
support the newer protocol versions.
</para>
</listitem>
</varlistentry>
<varlistentry id="guc-ssl-max-protocol-version" xreflabel="ssl_max_protocol_version">
<term><varname>ssl_max_protocol_version</varname> (<type>enum</type>)
<indexterm>
<primary><varname>ssl_max_protocol_version</varname> configuration parameter</primary>
</indexterm>
</term>
<listitem>
<para>
Sets the maximum SSL/TLS protocol version to use. Valid values are as
for <xref linkend="guc-ssl-min-protocol-version"/>, with addition of
an empty string, which allows any protocol version. The default is to
allow any version. Setting the maximum protocol version is mainly
useful for testing or if some component has issues working with a
newer protocol.
</para>
</listitem>
</varlistentry>
<varlistentry id="guc-ssl-dh-params-file" xreflabel="ssl_dh_params_file">
<term><varname>ssl_dh_params_file</varname> (<type>string</type>)
<indexterm>

View File

@ -67,6 +67,12 @@ static bool SSL_initialized = false;
static bool dummy_ssl_passwd_cb_called = false;
static bool ssl_is_server_start;
static int ssl_protocol_version_to_openssl(int v, const char *guc_name);
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
static int SSL_CTX_set_min_proto_version(SSL_CTX *ctx, int version);
static int SSL_CTX_set_max_proto_version(SSL_CTX *ctx, int version);
#endif
/* ------------------------------------------------------------ */
/* Public interface */
@ -183,8 +189,14 @@ be_tls_init(bool isServerStart)
goto error;
}
/* disallow SSL v2/v3 */
SSL_CTX_set_options(context, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
if (ssl_min_protocol_version)
SSL_CTX_set_min_proto_version(context,
ssl_protocol_version_to_openssl(ssl_min_protocol_version,
"ssl_min_protocol_version"));
if (ssl_max_protocol_version)
SSL_CTX_set_max_proto_version(context,
ssl_protocol_version_to_openssl(ssl_max_protocol_version,
"ssl_max_protocol_version"));
/* disallow SSL session tickets */
#ifdef SSL_OP_NO_TICKET /* added in OpenSSL 0.9.8f */
@ -1209,3 +1221,110 @@ X509_NAME_to_cstring(X509_NAME *name)
return result;
}
/*
* Convert TLS protocol version GUC enum to OpenSSL values
*
* This is a straightforward one-to-one mapping, but doing it this way makes
* guc.c independent of OpenSSL availability and version.
*
* If a version is passed that is not supported by the current OpenSSL
* version, then we throw an error, so that subsequent code can assume it's
* working with a supported version.
*/
static int
ssl_protocol_version_to_openssl(int v, const char *guc_name)
{
switch (v)
{
case PG_TLS_ANY:
return 0;
case PG_TLS1_VERSION:
return TLS1_VERSION;
case PG_TLS1_1_VERSION:
#ifdef TLS1_1_VERSION
return TLS1_1_VERSION;
#else
goto error;
#endif
case PG_TLS1_2_VERSION:
#ifdef TLS1_2_VERSION
return TLS1_2_VERSION;
#else
goto error;
#endif
case PG_TLS1_3_VERSION:
#ifdef TLS1_3_VERSION
return TLS1_3_VERSION;
#else
goto error;
#endif
}
error:
pg_attribute_unused();
ereport(ERROR,
(errmsg("%s setting %s not supported by this build",
guc_name,
GetConfigOption(guc_name, false, false))));
return -1;
}
/*
* Replacements for APIs present in newer versions of OpenSSL
*/
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
/*
* OpenSSL versions that support TLS 1.3 shouldn't get here because they
* already have these functions. So we don't have to keep updating the below
* code for every new TLS version, and eventually it can go away. But let's
* just check this to make sure ...
*/
#ifdef TLS1_3_VERSION
#error OpenSSL version mismatch
#endif
static int
SSL_CTX_set_min_proto_version(SSL_CTX *ctx, int version)
{
int ssl_options = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
if (version > TLS1_VERSION)
ssl_options |= SSL_OP_NO_TLSv1;
#ifdef TLS1_1_VERSION
if (version > TLS1_1_VERSION)
ssl_options |= SSL_OP_NO_TLSv1_1;
#endif
#ifdef TLS1_2_VERSION
if (version > TLS1_2_VERSION)
ssl_options |= SSL_OP_NO_TLSv1_2;
#endif
SSL_CTX_set_options(ctx, ssl_options);
return 1; /* success */
}
static int
SSL_CTX_set_max_proto_version(SSL_CTX *ctx, int version)
{
int ssl_options = 0;
AssertArg(version != 0);
#ifdef TLS1_1_VERSION
if (version < TLS1_1_VERSION)
ssl_options |= SSL_OP_NO_TLSv1_1;
#endif
#ifdef TLS1_2_VERSION
if (version < TLS1_2_VERSION)
ssl_options |= SSL_OP_NO_TLSv1_2;
#endif
SSL_CTX_set_options(ctx, ssl_options);
return 1; /* success */
}
#endif /* OPENSSL_VERSION_NUMBER */

View File

@ -60,6 +60,9 @@ char *SSLECDHCurve;
/* GUC variable: if false, prefer client ciphers */
bool SSLPreferServerCiphers;
int ssl_min_protocol_version;
int ssl_max_protocol_version;
/* ------------------------------------------------------------ */
/* Procedures common to all secure sessions */
/* ------------------------------------------------------------ */

View File

@ -428,6 +428,15 @@ static const struct config_enum_entry password_encryption_options[] = {
{NULL, 0, false}
};
const struct config_enum_entry ssl_protocol_versions_info[] = {
{"", PG_TLS_ANY, false},
{"TLSv1", PG_TLS1_VERSION, false},
{"TLSv1.1", PG_TLS1_1_VERSION, false},
{"TLSv1.2", PG_TLS1_2_VERSION, false},
{"TLSv1.3", PG_TLS1_3_VERSION, false},
{NULL, 0, false}
};
/*
* Options for enum values stored in other modules
*/
@ -4193,6 +4202,30 @@ static struct config_enum ConfigureNamesEnum[] =
NULL, NULL, NULL
},
{
{"ssl_min_protocol_version", PGC_SIGHUP, CONN_AUTH_SSL,
gettext_noop("Sets the minimum SSL/TLS protocol version to use."),
NULL,
GUC_SUPERUSER_ONLY
},
&ssl_min_protocol_version,
PG_TLS1_VERSION,
ssl_protocol_versions_info + 1 /* don't allow PG_TLS_ANY */,
NULL, NULL, NULL
},
{
{"ssl_max_protocol_version", PGC_SIGHUP, CONN_AUTH_SSL,
gettext_noop("Sets the maximum SSL/TLS protocol version to use."),
NULL,
GUC_SUPERUSER_ONLY
},
&ssl_max_protocol_version,
PG_TLS_ANY,
ssl_protocol_versions_info,
NULL, NULL, NULL
},
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, NULL, NULL, NULL, NULL

View File

@ -103,6 +103,8 @@
#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers
#ssl_prefer_server_ciphers = on
#ssl_ecdh_curve = 'prime256v1'
#ssl_min_protocol_version = 'TLSv1'
#ssl_max_protocol_version = ''
#ssl_dh_params_file = ''
#ssl_passphrase_command = ''
#ssl_passphrase_command_supports_reload = off

View File

@ -102,6 +102,17 @@ extern WaitEventSet *FeBeWaitSet;
extern char *SSLCipherSuites;
extern char *SSLECDHCurve;
extern bool SSLPreferServerCiphers;
extern int ssl_min_protocol_version;
extern int ssl_max_protocol_version;
enum ssl_protocol_versions
{
PG_TLS_ANY = 0,
PG_TLS1_VERSION,
PG_TLS1_1_VERSION,
PG_TLS1_2_VERSION,
PG_TLS1_3_VERSION,
};
/*
* prototypes for functions in be-secure-common.c