]> granicus.if.org Git - postgresql/commitdiff
Add settings to control SSL/TLS protocol version
authorPeter Eisentraut <peter_e@gmx.net>
Tue, 20 Nov 2018 20:49:01 +0000 (21:49 +0100)
committerPeter Eisentraut <peter_e@gmx.net>
Tue, 20 Nov 2018 21:12:10 +0000 (22:12 +0100)
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

doc/src/sgml/config.sgml
src/backend/libpq/be-secure-openssl.c
src/backend/libpq/be-secure.c
src/backend/utils/misc/guc.c
src/backend/utils/misc/postgresql.conf.sample
src/include/libpq/libpq.h

index c4effa034c12e6d8831bbbc88635f612e3fa9282..5d76862f461981b0e1d85e7d20a6d59bf372b1b5 100644 (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>
index 6a576572bbe8a6f3ec53b9103d75b5171dc081aa..b2b0cccdaedb1fd0927044b4b8a0595c4dcccc96 100644 (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 */
index 4eb21fe89de6c71530ffb2d0941e2f171d127e72..7cfafb59081d1d36ccf4c109584bda77b3776ba1 100644 (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                       */
 /* ------------------------------------------------------------ */
index 7e9e8c642bfcf99da3bc2123e406245190ab56f6..19c678f596f13fc913cb3568a34fb2a7af569496 100644 (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
index ab063dae4193bb7cc62010f986463319c2b9e8a0..26d5c4c967791de1896a11338fff9e55f85c0127 100644 (file)
 #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
index c7762f68a6bd10f7c9314c61a534bd4f9deb6558..e81752446e103976b68fe91e8b7fd8fd3782f790 100644 (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