Blob Blame History Raw
--- 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>: