postgresql-9.4-ssl-protocols.diff
text/x-patch
Filename: postgresql-9.4-ssl-protocols.diff
Type: text/x-patch
Part: 1
Patch
Format: unified
| File | + | − |
|---|---|---|
| doc/src/sgml/config.sgml | 28 | 0 |
| src/backend/libpq/be-secure.c | 110 | 5 |
| src/backend/utils/misc/guc.c | 16 | 0 |
| src/backend/utils/misc/postgresql.conf.sample | 1 | 0 |
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 2fb9217..50bd51f 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -1027,6 +1027,34 @@ include_dir 'conf.d'
</listitem>
</varlistentry>
+ <varlistentry id="guc-ssl-protocols" xreflabel="ssl_protocols">
+ <term><varname>ssl_protocols</varname> (<type>string</type>)</term>
+ <indexterm>
+ <primary><varname>ssl_protocols</> configuration parameter</primary>
+ </indexterm>
+ <listitem>
+ <para>
+ Specifies a colon-separated list of <acronym>SSL</> protocols that are
+ allowed to be used on secure connections. Each entry in the list can
+ be prefixed by a <literal>+</> (add to the current list)
+ or <literal>-</> (remove from the current list). If no prefix is used,
+ the list is replaced with the specified protocol.
+ </para>
+ <para>
+ The full list of supported protocols can be found in the
+ the <application>openssl</> manual page. In addition to the names of
+ individual protocols, the following keywords can be
+ used: <literal>ALL</> (all protocols supported by the underlying
+ crypto library), <literal>SSL</> (all supported versions
+ of <acronym>SSL</>) and <literal>TLS</> (all supported versions
+ of <acronym>TLS</>).
+ </para>
+ <para>
+ The default is <literal>ALL:-SSL</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry id="guc-ssl-ciphers" xreflabel="ssl_ciphers">
<term><varname>ssl_ciphers</varname> (<type>string</type>)
<indexterm>
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index 89c30d0..acebdc2 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -90,6 +90,7 @@ static void initialize_SSL(void);
static int open_server_SSL(Port *);
static void close_SSL(Port *);
static const char *SSLerrmessage(void);
+static long parse_SSL_protocols(const char *str);
#endif
char *ssl_cert_file;
@@ -112,7 +113,8 @@ static SSL_CTX *SSL_context = NULL;
static bool ssl_loaded_verify_locations = false;
#endif
-/* GUC variable controlling SSL cipher list */
+/* GUC variables controlling SSL protocol and cipher list */
+char *SSLProtocols = NULL;
char *SSLCipherSuites = NULL;
/* GUC variable for default ECHD curve. */
@@ -887,15 +889,16 @@ initialize_SSL(void)
SSLerrmessage())));
}
- /* set up ephemeral DH keys, and disallow SSL v2/v3 while at it */
+ /* set up ephemeral DH keys */
SSL_CTX_set_tmp_dh_callback(SSL_context, tmp_dh_cb);
- SSL_CTX_set_options(SSL_context,
- SSL_OP_SINGLE_DH_USE |
- SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
+ SSL_CTX_set_options(SSL_context, SSL_OP_SINGLE_DH_USE);
/* set up ephemeral ECDH keys */
initialize_ecdh();
+ /* set up the allowed protocol list */
+ SSL_CTX_set_options(SSL_context, parse_SSL_protocols(SSLProtocols));
+
/* set up the allowed cipher list */
if (SSL_CTX_set_cipher_list(SSL_context, SSLCipherSuites) != 1)
elog(FATAL, "could not set the cipher list (no valid ciphers available)");
@@ -1158,4 +1161,106 @@ SSLerrmessage(void)
return errbuf;
}
+/*
+ * Parse the SSL allowed protocol list
+ *
+ * The logic here is inverted. OpenSSL does not take a list of
+ * protocols to use, but a list of protocols to avoid. We use the
+ * same bits with the opposite meaning, then invert the result.
+ */
+
+#define SSL_PROTO_SSLv2 SSL_OP_NO_SSLv2
+#define SSL_PROTO_SSLv3 SSL_OP_NO_SSLv3
+#define SSL_PROTO_SSL (SSL_PROTO_SSLv2 | SSL_PROTO_SSLv3)
+#define SSL_PROTO_TLSv1 SSL_OP_NO_TLSv1
+#ifdef SSL_OP_NO_TLSv1_1
+#define SSL_PROTO_TLSv1_1 SSL_OP_NO_TLSv1_1
+#else
+#define SSL_PROTO_TLSv1_1 0
+#endif
+#ifdef SSL_OP_NO_TLSv1_2
+#define SSL_PROTO_TLSv1_2 SSL_OP_NO_TLSv1_2
+#else
+#define SSL_PROTO_TLSv1_2 0
+#endif
+#define SSL_PROTO_TLS (SSL_PROTO_TLSv1 | SSL_PROTO_TLSv1_1 | SSL_PROTO_TLSv1_2)
+#define SSL_PROTO_ALL (SSL_PROTO_SSL | SSL_PROTO_TLS)
+#define SSL_PROTO_NONE 0
+
+#define str_is_token(str, tok, len) \
+ (len == sizeof(tok) - 1 && pg_strncasecmp(str, tok, len) == 0)
+
+static long
+parse_SSL_protocols(const char *str)
+{
+ long current, result;
+ const char *p, *q;
+ int action;
+
+ /*
+ * Iterate over the colon-separated list of protocols. If a protocol is
+ * preceded by a +, it is added to the list. If it is preceded by a -, it
+ * is removed from the list. If it is not preceded by anything, the list
+ * is set to exactly that protocol. "ALL" can be used to indicate all
+ * protocols, "NONE" to indicate no protocols "SSL" to indicate all SSL
+ * protocols and "TLS" to indicate all TLS protocols. The parser accepts
+ * "SSLv2", "SSLv3" and "SSL", but they are removed after parsing.
+ */
+ result = SSL_PROTO_NONE;
+ for (p = q = str; *q; p = q + 1) {
+ for (q = p; *q && *q != ':'; ++q)
+ /* nothing */ ;
+ if (*p == '-' || *p == '+')
+ action = *p++;
+ else
+ action = '=';
+ if (str_is_token(p, "ALL", q - p))
+ current = SSL_PROTO_ALL;
+ else if (str_is_token(p, "NONE", q - p))
+ current = SSL_PROTO_NONE;
+ else if (str_is_token(p, SSL_TXT_SSLV2, q - p))
+ current = SSL_PROTO_SSLv2;
+ else if (str_is_token(p, SSL_TXT_SSLV3, q - p))
+ current = SSL_PROTO_SSLv3;
+ else if (str_is_token(p, "SSL", q - p))
+ current = SSL_PROTO_SSL;
+ else if (str_is_token(p, SSL_TXT_TLSV1, q - p))
+ current = SSL_PROTO_TLSv1;
+#ifdef SSL_OP_NO_TLSv1_1
+ else if (str_is_token(p, SSL_TXT_TLSV1_1, q - p))
+ current = SSL_PROTO_TLSv1_1;
+#endif
+#ifdef SSL_OP_NO_TLSv1_2
+ else if (str_is_token(p, SSL_TXT_TLSV1_2, q - p))
+ current = SSL_PROTO_TLSv1_2;
+#endif
+ else if (str_is_token(p, "TLS", q - p))
+ current = SSL_PROTO_TLS;
+ else
+ elog(FATAL, "invalid SSL protocol list");
+ switch (action) {
+ case '+':
+ result |= current;
+ break;
+ case '-':
+ result &= ~current;
+ break;
+ default:
+ result = current;
+ break;
+ }
+ }
+ /* forcibly disallow SSLv2 and SSLv3 */
+ if (result & SSL_PROTO_SSL) {
+ elog(WARNING, "removing SSLv2 and SSLv3 from SSL protocol list");
+ result &= ~SSL_PROTO_SSL;
+ }
+ /* check the result */
+ if (result == 0)
+ elog(FATAL, "could not set the protocol list (no valid protocols available)");
+ elog(DEBUG2, "enabling SSL protocols: %lx", result);
+ /* return the inverse */
+ return (SSL_PROTO_ALL & ~result);
+}
+
#endif /* USE_SSL */
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index ea58cf6..3d1b668 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -125,6 +125,7 @@ extern char *default_tablespace;
extern char *temp_tablespaces;
extern bool ignore_checksum_failure;
extern bool synchronize_seqscans;
+extern char *SSLProtocols;
extern char *SSLCipherSuites;
extern char *SSLECDHCurve;
extern bool SSLPreferServerCiphers;
@@ -3215,6 +3216,21 @@ static struct config_string ConfigureNamesString[] =
},
{
+ {"ssl_protocols", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ gettext_noop("Sets the list of allowed SSL protocols."),
+ NULL,
+ GUC_SUPERUSER_ONLY
+ },
+ &SSLProtocols,
+#ifdef USE_SSL
+ "ALL:-SSL",
+#else
+ "none",
+#endif
+ NULL, NULL, NULL
+ },
+
+ {
{"ssl_ciphers", PGC_POSTMASTER, CONN_AUTH_SECURITY,
gettext_noop("Sets the list of allowed SSL ciphers."),
NULL,
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 8d5bb19..d161113 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -79,6 +79,7 @@
#authentication_timeout = 1min # 1s-600s
#ssl = off # (change requires restart)
+#ssl_protocols = 'ALL:-SSL' # allowed SSL protocols
#ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers
# (change requires restart)
#ssl_prefer_server_ciphers = on # (change requires restart)