The attached patches add an ssl_protocols configuration option which
control which versions of SSL or TLS the server will use.  The syntax is
similar to Apache's SSLProtocols directive, except that the list is
colon-separated instead of whitespace-separated, although that is easy
to change if it proves unpopular.

Summary of the patch:

 - In src/backend/libpq/be-secure.c:
   - Add an SSLProtocols variable for the option.
   - Add a function, parse_SSL_protocols(), that parses an ssl_protocols
     string and returns a bitmask suitable for SSL_CTX_set_options().
   - Change initialize_SSL() to call parse_SSL_protocols() and pass the
     result to SSL_CTX_set_options().
 - In src/backend/utils/misc/guc.c:
   - Add an extern declaration for SSLProtocols.
   - Add an entry in the ConfigureNamesString array for the
     ssl_protocols option.
 - In src/backend/utils/misc/postgresql.conf.sample:
   - Add a sample ssl_protocols line.
 - In doc/src/sgml/config.sgml:
   - Document the ssl_protocols option.

The file names are slightly different in 9.5, since be-secure.c was
split in two and the declaration was moved into libpq.h.

The default is "ALL:-SSLv2" in 9.0-9.3 and "ALL:-SSL" in 9.4 and up.
This corresponds to the current hardcoded values, so the default
behavior is unchanged, but the admin now has the option to select a
different settings, e.g. if a serious vulnerability is found in TLS 1.0.

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 6ee17d8..7233a73 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-openssl.c b/src/backend/libpq/be-secure-openssl.c
index b05364c..f440b77 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -87,6 +87,7 @@ static int	verify_cb(int, X509_STORE_CTX *);
 static void info_cb(const SSL *ssl, int type, int args);
 static void initialize_ecdh(void);
 static const char *SSLerrmessage(void);
+static long parse_SSL_protocols(const char *str);
 
 /* are we in the middle of a renegotiation? */
 static bool in_ssl_renegotiation = false;
@@ -245,15 +246,16 @@ be_tls_init(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)");
@@ -1053,3 +1055,106 @@ SSLerrmessage(void)
 	snprintf(errbuf, sizeof(errbuf), _("SSL error code %lu"), errcode);
 	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);
+}
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index 41ec1ad..a5a5f3f 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -52,7 +52,8 @@ int			ssl_renegotiation_limit;
 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. */
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index dca533a..3a34be1 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -3239,6 +3239,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 dac6776..6b21fc5 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)
diff --git a/src/include/libpq/libpq.h b/src/include/libpq/libpq.h
index 5da9d8d..15d0c3f 100644
--- a/src/include/libpq/libpq.h
+++ b/src/include/libpq/libpq.h
@@ -88,6 +88,7 @@ extern ssize_t secure_raw_write(Port *port, const void *ptr, size_t len);
 extern bool ssl_loaded_verify_locations;
 
 /* GUCs */
+extern char *SSLProtocols;
 extern char *SSLCipherSuites;
 extern char *SSLECDHCurve;
 extern bool SSLPreferServerCiphers;
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)
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index c9276a3..ab2941b 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -873,6 +873,34 @@ include 'filename'
       </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:-SSLv2</literal>.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-ssl-ciphers" xreflabel="ssl_ciphers">
       <term><varname>ssl_ciphers</varname> (<type>string</type>)</term>
       <indexterm>
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index d1beda8..c5d1558 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -87,6 +87,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;
@@ -106,7 +107,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;
 
 /* ------------------------------------------------------------ */
@@ -793,9 +795,12 @@ initialize_SSL(void)
 							SSLerrmessage())));
 	}
 
-	/* set up ephemeral DH keys, and disallow SSL v2 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_CTX_set_options(SSL_context, SSL_OP_SINGLE_DH_USE);
+
+	/* 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)
@@ -1055,4 +1060,107 @@ 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", but it
+	 * is 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 */
+	if (result & SSL_PROTO_SSLv2) {
+		elog(WARNING, "removing SSLv2 from SSL protocol list");
+		result &= ~SSL_PROTO_SSLv2;
+	}
+	/* 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 2b6527f..42d4ad1 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -125,6 +125,7 @@ extern char *temp_tablespaces;
 extern bool ignore_checksum_failure;
 extern bool synchronize_seqscans;
 extern int	ssl_renegotiation_limit;
+extern char *SSLProtocols;
 extern char *SSLCipherSuites;
 
 #ifdef TRACE_SORT
@@ -3117,6 +3118,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:-SSLv2",
+#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 12f1cba..64d38a9 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:-SSLv2'		# allowed SSL protocols
 #ssl_ciphers = 'DEFAULT:!LOW:!EXP:!MD5:@STRENGTH'	# allowed SSL ciphers
 					# (change requires restart)
 #ssl_renegotiation_limit = 512MB	# amount of data between renegotiations
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 51d7da9..628f523 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -789,6 +789,34 @@ SET ENABLE_SEQSCAN TO OFF;
       </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:-SSLv2</literal>.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-ssl-ciphers" xreflabel="ssl_ciphers">
       <term><varname>ssl_ciphers</varname> (<type>string</type>)</term>
       <indexterm>
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index bccea54..9b8871b 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -87,6 +87,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;
@@ -106,7 +107,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;
 
 /* ------------------------------------------------------------ */
@@ -793,9 +795,12 @@ initialize_SSL(void)
 							SSLerrmessage())));
 	}
 
-	/* set up ephemeral DH keys, and disallow SSL v2 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_CTX_set_options(SSL_context, SSL_OP_SINGLE_DH_USE);
+
+	/* 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)
@@ -1055,4 +1060,107 @@ 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", but it
+	 * is 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 */
+	if (result & SSL_PROTO_SSLv2) {
+		elog(WARNING, "removing SSLv2 from SSL protocol list");
+		result &= ~SSL_PROTO_SSLv2;
+	}
+	/* 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 e5ee0f8..cc8edbc 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -132,6 +132,7 @@ extern char *default_tablespace;
 extern char *temp_tablespaces;
 extern bool synchronize_seqscans;
 extern int	ssl_renegotiation_limit;
+extern char *SSLProtocols;
 extern char *SSLCipherSuites;
 
 #ifdef TRACE_SORT
@@ -3043,6 +3044,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:-SSLv2",
+#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 6fe6924..8cca6b9 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -78,6 +78,7 @@
 
 #authentication_timeout = 1min		# 1s-600s
 #ssl = off				# (change requires restart)
+#ssl_protocols = 'ALL:-SSLv2'		# allowed SSL protocols
 #ssl_ciphers = 'ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH'	# allowed SSL ciphers
 					# (change requires restart)
 #ssl_renegotiation_limit = 512MB	# amount of data between renegotiations
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 179c60e..4ae3ae3 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -689,6 +689,34 @@ SET ENABLE_SEQSCAN TO OFF;
       </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:-SSLv2</literal>.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-ssl-ciphers" xreflabel="ssl_ciphers">
       <term><varname>ssl_ciphers</varname> (<type>string</type>)</term>
       <indexterm>
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index 6e09496..1d3c09b 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -92,6 +92,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
 
 /*
@@ -106,7 +107,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;
 
 /* ------------------------------------------------------------ */
@@ -793,9 +795,12 @@ initialize_SSL(void)
 							SSLerrmessage())));
 	}
 
-	/* set up ephemeral DH keys, and disallow SSL v2 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_CTX_set_options(SSL_context, SSL_OP_SINGLE_DH_USE);
+
+	/* 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)
@@ -1074,4 +1079,107 @@ 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", but it
+	 * is 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 */
+	if (result & SSL_PROTO_SSLv2) {
+		elog(WARNING, "removing SSLv2 from SSL protocol list");
+		result &= ~SSL_PROTO_SSLv2;
+	}
+	/* 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 a66a7d9..c2dc017 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -131,6 +131,7 @@ extern char *temp_tablespaces;
 extern bool synchronize_seqscans;
 extern bool fullPageWrites;
 extern int	ssl_renegotiation_limit;
+extern char *SSLProtocols;
 extern char *SSLCipherSuites;
 
 #ifdef TRACE_SORT
@@ -2987,6 +2988,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:-SSLv2",
+#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 82c8ae4..8acc5a5 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -78,6 +78,7 @@
 
 #authentication_timeout = 1min		# 1s-600s
 #ssl = off				# (change requires restart)
+#ssl_protocols = 'ALL:-SSLv2'		# allowed SSL protocols
 #ssl_ciphers = 'ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH'	# allowed SSL ciphers
 					# (change requires restart)
 #ssl_renegotiation_limit = 512MB	# amount of data between renegotiations
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 2f37a29..71efa6f 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -685,6 +685,34 @@ SET ENABLE_SEQSCAN TO OFF;
       </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:-SSLv2</literal>.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-ssl-ciphers" xreflabel="ssl_ciphers">
       <term><varname>ssl_ciphers</varname> (<type>string</type>)</term>
       <indexterm>
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index 28e3102..e10bcdd 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -92,6 +92,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
 
 /*
@@ -105,7 +106,8 @@ int			ssl_renegotiation_limit;
 static SSL_CTX *SSL_context = NULL;
 static bool ssl_loaded_verify_locations = false;
 
-/* GUC variable controlling SSL cipher list */
+/* GUC variables controlling SSL protocol and cipher list */
+char	   *SSLProtocols = NULL;
 char	   *SSLCipherSuites = NULL;
 #endif
 
@@ -793,9 +795,12 @@ initialize_SSL(void)
 							SSLerrmessage())));
 	}
 
-	/* set up ephemeral DH keys, and disallow SSL v2 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_CTX_set_options(SSL_context, SSL_OP_SINGLE_DH_USE);
+
+	/* 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)
@@ -1074,4 +1079,107 @@ 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", but it
+	 * is 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 */
+	if (result & SSL_PROTO_SSLv2) {
+		elog(WARNING, "removing SSLv2 from SSL protocol list");
+		result &= ~SSL_PROTO_SSLv2;
+	}
+	/* 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 718de95..563267e 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -130,6 +130,7 @@ extern bool optimize_bounded_sort;
 #endif
 
 #ifdef USE_SSL
+extern char *SSLProtocols;
 extern char *SSLCipherSuites;
 #endif
 
@@ -2638,6 +2639,21 @@ static struct config_string ConfigureNamesString[] =
 
 #ifdef USE_SSL
 	{
+		{"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:-SSLv2",
+#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 155af1c..b2e1023 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -78,6 +78,7 @@
 
 #authentication_timeout = 1min		# 1s-600s
 #ssl = off				# (change requires restart)
+#ssl_protocols = 'ALL:-SSLv2'		# allowed SSL protocols
 #ssl_ciphers = 'ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH'	# allowed SSL ciphers
 					# (change requires restart)
 #ssl_renegotiation_limit = 512MB	# amount of data between renegotiations
DES
-- 
Dag-Erling Smørgrav - d...@des.no
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to