--- contrib/mod_tls.c
+++ contrib/mod_tls.c
@@ -376,6 +376,8 @@ static char *tls_passphrase_provider = N
#define TLS_PROTO_SSL_V3 0x0001
#define TLS_PROTO_TLS_V1 0x0002
+#define TLS_PROTO_TLS_V1_1 0x0004
+#define TLS_PROTO_TLS_V1_2 0x0008
static unsigned int tls_protocol = TLS_PROTO_SSL_V3|TLS_PROTO_TLS_V1;
static int tls_ssl_opts = SSL_OP_ALL|SSL_OP_NO_SSLv2|SSL_OP_SINGLE_DH_USE^SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
@@ -2194,25 +2196,101 @@ static int tls_init_ctx(void) {
return 0;
}
+static const char *tls_get_proto_str(pool *p, unsigned int protos,
+ unsigned int *count) {
+ char *proto_str = "";
+ unsigned int nproto = 0;
+
+ if (protos & TLS_PROTO_SSL_V3) {
+ proto_str = pstrcat(p, proto_str, *proto_str ? ", " : "",
+ "SSLv3", NULL);
+ nproto++;
+ }
+
+ if (protos & TLS_PROTO_TLS_V1) {
+ proto_str = pstrcat(p, proto_str, *proto_str ? ", " : "",
+ "TLSv1", NULL);
+ nproto++;
+ }
+
+ if (protos & TLS_PROTO_TLS_V1_1) {
+ proto_str = pstrcat(p, proto_str, *proto_str ? ", " : "",
+ "TLSv1.1", NULL);
+ nproto++;
+ }
+
+ if (protos & TLS_PROTO_TLS_V1_2) {
+ proto_str = pstrcat(p, proto_str, *proto_str ? ", " : "",
+ "TLSv1.2", NULL);
+ nproto++;
+ }
+
+ *count = nproto;
+ return proto_str;
+}
+
+/* Construct the options value that disables all unsupported protocols.
+ */
+static int get_disabled_protocols(unsigned int supported_protocols) {
+ int disabled_protocols;
+
+ /* First, create an options value where ALL protocols are disabled. */
+ disabled_protocols = (SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1);
+
+#ifdef SSL_OP_NO_TLSv1_1
+ disabled_protocols |= SSL_OP_NO_TLSv1_1;
+#endif
+#ifdef SSL_OP_NO_TLSv1_2
+ disabled_protocols |= SSL_OP_NO_TLSv1_2;
+#endif
+
+ /* Now, based on the given bitset of supported protocols, clear the
+ * necessary bits.
+ */
+
+ if (supported_protocols & TLS_PROTO_SSL_V3) {
+ disabled_protocols &= ~SSL_OP_NO_SSLv3;
+ }
+
+ if (supported_protocols & TLS_PROTO_TLS_V1) {
+ disabled_protocols &= ~SSL_OP_NO_TLSv1;
+ }
+
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L
+ if (supported_protocols & TLS_PROTO_TLS_V1_1) {
+ disabled_protocols &= ~SSL_OP_NO_TLSv1_1;
+ }
+
+ if (supported_protocols & TLS_PROTO_TLS_V1_2) {
+ disabled_protocols &= ~SSL_OP_NO_TLSv1_2;
+ }
+#endif /* OpenSSL-1.0.1 or later */
+
+ return disabled_protocols;
+}
+
static int tls_init_server(void) {
#if OPENSSL_VERSION_NUMBER > 0x000907000L
config_rec *c = NULL;
#endif
char *tls_ca_cert = NULL, *tls_ca_path = NULL;
+ unsigned int enabled_proto_count = 0;
+ int disabled_proto;
+ const char *enabled_proto_str = NULL;
+
+ disabled_proto = get_disabled_protocols(tls_protocol);
+
+ /* Per the comments in <ssl/ssl.h>, SSL_CTX_set_options() uses |= on
+ * the previous value. This means we can easily OR in our new option
+ * values with any previously set values.
+ */
+ enabled_proto_str = tls_get_proto_str(main_server->pool, tls_protocol,
+ &enabled_proto_count);
- if ((tls_protocol & TLS_PROTO_SSL_V3) &&
- (tls_protocol & TLS_PROTO_TLS_V1)) {
- /* This is the default, so there is no need to do anything. */
- pr_log_debug(DEBUG8, MOD_TLS_VERSION ": supporting SSLv3, TLSv1 protocols");
-
- } else if (tls_protocol & TLS_PROTO_SSL_V3) {
- SSL_CTX_set_ssl_version(ssl_ctx, SSLv3_server_method());
- pr_log_debug(DEBUG8, MOD_TLS_VERSION ": supporting SSLv3 protocol only");
-
- } else if (tls_protocol & TLS_PROTO_TLS_V1) {
- SSL_CTX_set_ssl_version(ssl_ctx, TLSv1_server_method());
- pr_log_debug(DEBUG8, MOD_TLS_VERSION ": supporting TLSv1 protocol only");
- }
+ pr_log_debug(DEBUG8, MOD_TLS_VERSION ": supporting %s %s",
+ enabled_proto_str,
+ enabled_proto_count != 1 ? "protocols" : "protocol only");
+ SSL_CTX_set_options(ssl_ctx, disabled_proto);
tls_ca_cert = get_param_ptr(main_server->conf, "TLSCACertificateFile", FALSE);
tls_ca_path = get_param_ptr(main_server->conf, "TLSCACertificatePath", FALSE);
@@ -6319,16 +6397,30 @@ MODRET set_tlsprotocol(cmd_rec *cmd) {
tls_protocol = 0;
for (i = 1; i < cmd->argc; i++) {
- if (strcasecmp(cmd->argv[i], "SSLv23") == 0) {
+ if (strncasecmp(cmd->argv[i], "SSLv23", 7) == 0) {
tls_protocol |= TLS_PROTO_SSL_V3;
tls_protocol |= TLS_PROTO_TLS_V1;
- } else if (strcasecmp(cmd->argv[i], "SSLv3") == 0) {
+ } else if (strncasecmp(cmd->argv[i], "SSLv3", 6) == 0) {
tls_protocol |= TLS_PROTO_SSL_V3;
- } else if (strcasecmp(cmd->argv[i], "TLSv1") == 0) {
+ } else if (strncasecmp(cmd->argv[i], "TLSv1", 6) == 0) {
tls_protocol |= TLS_PROTO_TLS_V1;
+ } else if (strncasecmp(cmd->argv[i], "TLSv1.1", 8) == 0) {
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L
+ tls_protocol |= TLS_PROTO_TLS_V1_1;
+#else
+ CONF_ERROR(cmd, "Your OpenSSL installation does not support TLSv1.1");
+#endif /* OpenSSL 1.0.1 or later */
+
+ } else if (strncasecmp(cmd->argv[i], "TLSv1.2", 8) == 0) {
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L
+ tls_protocol |= TLS_PROTO_TLS_V1_2;
+#else
+ CONF_ERROR(cmd, "Your OpenSSL installation does not support TLSv1.2");
+#endif /* OpenSSL 1.0.1 or later */
+
} else {
CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unknown protocol: '",
cmd->argv[i], "'", NULL));
--- doc/contrib/mod_tls.html
+++ doc/contrib/mod_tls.html
@@ -999,6 +999,17 @@ The allowed protocols are:
<td><code>TLSv1</code></td>
<td>Allow only TLSv1</td>
</tr>
+
+ <tr>
+ <td><code>TLSv1.1</code></td>
+ <td>Allow only TLSv1.1</td>
+ </tr>
+
+ <tr>
+ <td><code>TLSv1.2</code></td>
+ <td>Allow only TLSv1.2</td>
+ </tr>
+
</table>
To support both SSLv3 and TLSv1, simply list both parameters for the
<code>TLSProtocol</code> directive, <i>e.g.</i>: