--- 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_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: TLSv1 Allow only TLSv1 + + + TLSv1.1 + Allow only TLSv1.1 + + + + TLSv1.2 + Allow only TLSv1.2 + + To support both SSLv3 and TLSv1, simply list both parameters for the TLSProtocol directive, e.g.: