]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: ssl: ssl_methods implementation is reworked and factored for min/max tlsxx
authorEmmanuel Hocdet <manu@gandi.net>
Thu, 30 Mar 2017 17:19:37 +0000 (19:19 +0200)
committerWilly Tarreau <w@1wt.eu>
Fri, 12 May 2017 13:49:04 +0000 (15:49 +0200)
Plan is to add min-tlsxx max-tlsxx configuration, more consistent than no-tlsxx.
This patch introduce internal min/max and replace force-tlsxx implementation.
SSL method configuration is store in 'struct tls_version_filter'.
SSL method configuration to openssl setting is abstract in 'methodVersions' table.
With openssl < 1.1.0, SSL_CTX_set_ssl_version is used for force (min == max).
With openssl >= 1.1.0, SSL_CTX_set_min/max_proto_version is used.

include/types/listener.h
include/types/server.h
include/types/ssl_sock.h
src/ssl_sock.c

index 8aae395e0d6b1d3733ae549e4b7ea70750d15baa..93f36624c609ff839e360e54e041fba316c50583 100644 (file)
@@ -27,6 +27,7 @@
 
 #ifdef USE_OPENSSL
 #include <openssl/ssl.h>
+#include <types/ssl_sock.h>
 #endif
 
 #include <common/config.h>
@@ -101,18 +102,7 @@ enum li_state {
  */
 
 #ifdef USE_OPENSSL
-/* bind_conf ssl options */
 #define BC_SSL_O_NONE           0x0000
-#define BC_SSL_O_NO_SSLV3       0x0001 /* disable SSLv3 */
-#define BC_SSL_O_NO_TLSV10      0x0002 /* disable TLSv10 */
-#define BC_SSL_O_NO_TLSV11      0x0004 /* disable TLSv11 */
-#define BC_SSL_O_NO_TLSV12      0x0008 /* disable TLSv12 */
-/* 0x000F reserved for 'no' protocol version options */
-#define BC_SSL_O_USE_SSLV3      0x0010 /* force SSLv3 */
-#define BC_SSL_O_USE_TLSV10     0x0020 /* force TLSv10 */
-#define BC_SSL_O_USE_TLSV11     0x0040 /* force TLSv11 */
-#define BC_SSL_O_USE_TLSV12     0x0080 /* force TLSv12 */
-/* 0x00F0 reserved for 'force' protocol version options */
 #define BC_SSL_O_NO_TLS_TICKETS 0x0100 /* disable session resumption tickets */
 #define BC_SSL_O_PREF_CLIE_CIPH 0x0200  /* prefer client ciphers */
 #endif
@@ -148,6 +138,7 @@ struct bind_conf {
        struct ssl_bind_conf *default_ssl_conf; /* custom SSL conf of default_ctx */
        int strict_sni;            /* refuse negotiation if sni doesn't match a certificate */
        int ssl_options;           /* ssl options */
+       struct tls_version_filter ssl_methods; /* ssl methods */
        struct eb_root sni_ctx;    /* sni_ctx tree of all known certs full-names sorted by name */
        struct eb_root sni_w_ctx;  /* sni_ctx tree of all known certs wildcards sorted by name */
        struct tls_keys_ref *keys_ref; /* TLS ticket keys reference */
index 83b1e80c6f9d1472fd792f339e6ff551baf31275..bf4eae14b1d5a3e2ee734fcb9e19fe91c5834c84 100644 (file)
@@ -27,6 +27,7 @@
 
 #ifdef USE_OPENSSL
 #include <openssl/ssl.h>
+#include <types/ssl_sock.h>
 #endif
 
 #include <common/config.h>
@@ -161,18 +162,6 @@ enum srv_initaddr {
 #ifdef USE_OPENSSL
 /* server ssl options */
 #define SRV_SSL_O_NONE         0x0000
-#define SRV_SSL_O_NO_VMASK     0x000F /* force version mask */
-#define SRV_SSL_O_NO_SSLV3     0x0001 /* disable SSLv3 */
-#define SRV_SSL_O_NO_TLSV10    0x0002 /* disable TLSv1.0 */
-#define SRV_SSL_O_NO_TLSV11    0x0004 /* disable TLSv1.1 */
-#define SRV_SSL_O_NO_TLSV12    0x0008 /* disable TLSv1.2 */
-/* 0x000F reserved for 'no' protocol version options */
-#define SRV_SSL_O_USE_VMASK    0x00F0 /* force version mask */
-#define SRV_SSL_O_USE_SSLV3    0x0010 /* force SSLv3 */
-#define SRV_SSL_O_USE_TLSV10   0x0020 /* force TLSv1.0 */
-#define SRV_SSL_O_USE_TLSV11   0x0040 /* force TLSv1.1 */
-#define SRV_SSL_O_USE_TLSV12   0x0080 /* force TLSv1.2 */
-/* 0x00F0 reserved for 'force' protocol version options */
 #define SRV_SSL_O_NO_TLS_TICKETS 0x0100 /* disable session resumption tickets */
 #define SRV_SSL_O_NO_REUSE     0x200  /* disable session reuse */
 #endif
@@ -281,6 +270,7 @@ struct server {
                SSL_SESSION *reused_sess;
                char *ciphers;                  /* cipher suite to use if non-null */
                int options;                    /* ssl options */
+               struct tls_version_filter methods;      /* ssl methods */
                int verify;                     /* verify method (set of SSL_VERIFY_* flags) */
                char *verify_host;              /* hostname of certificate must match this host */
                char *ca_file;                  /* CAfile to use on verify */
index e3a85ca3cad330224dec86daf1a7421977cc42d6..ecdad46c4964cf98dc0bdc6a5528177d19371c8b 100644 (file)
@@ -22,7 +22,6 @@
 #ifndef _TYPES_SSL_SOCK_H
 #define _TYPES_SSL_SOCK_H
 
-#include <types/listener.h>
 #include <openssl/ssl.h>
 #include <ebmbtree.h>
 
@@ -35,6 +34,12 @@ struct sni_ctx {
        struct ebmb_node name;    /* node holding the servername value */
 };
 
+struct tls_version_filter {
+       uint16_t flags;     /* ssl options */
+       uint8_t  min;      /* min TLS version */
+       uint8_t  max;      /* max TLS version */
+};
+
 extern struct list tlskeys_reference;
 
 struct tls_sess_key {
index a0f21cff16769dab86b29cec72326a5b4b27bd71..c43a330847fe36a361dd7498f5af6329379a3028 100644 (file)
 #define HASH_FUNCT EVP_sha256
 #endif /* OPENSSL_NO_SHA256 */
 
+/* ssl_methods flags for ssl options */
+#define MC_SSL_O_ALL            0x0000
+#define MC_SSL_O_NO_SSLV3       0x0001 /* disable SSLv3 */
+#define MC_SSL_O_NO_TLSV10      0x0002 /* disable TLSv10 */
+#define MC_SSL_O_NO_TLSV11      0x0004 /* disable TLSv11 */
+#define MC_SSL_O_NO_TLSV12      0x0008 /* disable TLSv12 */
+
+/* ssl_methods versions */
+enum {
+       CONF_TLSV_NONE = 0,
+       CONF_TLSV_MIN  = 1,
+       CONF_SSLV3     = 1,
+       CONF_TLSV10    = 2,
+       CONF_TLSV11    = 3,
+       CONF_TLSV12    = 4,
+       CONF_TLSV_MAX  = 4,
+};
+
 /* server and bind verify method, it uses a global value as default */
 enum {
        SSL_SOCK_VERIFY_DEFAULT  = 0,
@@ -140,6 +158,8 @@ static struct {
        char *connect_default_ciphers;
        int listen_default_ssloptions;
        int connect_default_ssloptions;
+       struct tls_version_filter listen_default_sslmethods;
+       struct tls_version_filter connect_default_sslmethods;
 
        int private_cache; /* Force to use a private session cache even if nbproc > 1 */
        unsigned int life_time;   /* SSL session lifetime in seconds */
@@ -157,6 +177,13 @@ static struct {
        .listen_default_ssloptions = BC_SSL_O_NONE,
        .connect_default_ssloptions = SRV_SSL_O_NONE,
 
+       .listen_default_sslmethods.flags = MC_SSL_O_ALL,
+       .listen_default_sslmethods.min = CONF_TLSV_NONE,
+       .listen_default_sslmethods.max = CONF_TLSV_NONE,
+       .connect_default_sslmethods.flags = MC_SSL_O_ALL,
+       .connect_default_sslmethods.min = CONF_TLSV_NONE,
+       .connect_default_sslmethods.max = CONF_TLSV_NONE,
+
 #ifdef DEFAULT_SSL_MAX_RECORD
        .max_record = DEFAULT_SSL_MAX_RECORD,
 #endif
@@ -3167,13 +3194,70 @@ int ssl_sock_load_cert_list_file(char *file, struct bind_conf *bind_conf, struct
 #define SSL_MODE_SMALL_BUFFERS 0
 #endif
 
+#if (OPENSSL_VERSION_NUMBER < 0x1010000fL) && !defined(OPENSSL_IS_BORINGSSL)
+static void ssl_set_SSLv3_func(SSL_CTX *ctx, int is_server)
+{
+#if SSL_OP_NO_SSLv3
+       is_server ? SSL_CTX_set_ssl_version(ctx, SSLv3_server_method())
+               : SSL_CTX_set_ssl_version(ctx, SSLv3_client_method());
+#endif
+}
+static void ssl_set_TLSv10_func(SSL_CTX *ctx, int is_server) {
+       is_server ? SSL_CTX_set_ssl_version(ctx, TLSv1_server_method())
+               : SSL_CTX_set_ssl_version(ctx, TLSv1_client_method());
+}
+static void ssl_set_TLSv11_func(SSL_CTX *ctx, int is_server) {
+#if SSL_OP_NO_TLSv1_1
+       is_server ? SSL_CTX_set_ssl_version(ctx, TLSv1_1_server_method())
+               : SSL_CTX_set_ssl_version(ctx, TLSv1_1_client_method());
+#endif
+}
+static void ssl_set_TLSv12_func(SSL_CTX *ctx, int is_server) {
+#if SSL_OP_NO_TLSv1_2
+       is_server ? SSL_CTX_set_ssl_version(ctx, TLSv1_2_server_method())
+               : SSL_CTX_set_ssl_version(ctx, TLSv1_2_client_method());
+#endif
+}
+#else /* openssl >= 1.1.0 */
+static void ssl_set_SSLv3_func(SSL_CTX *ctx, int is_max) {
+       is_max ? SSL_CTX_set_max_proto_version(ctx, SSL3_VERSION)
+               : SSL_CTX_set_min_proto_version(ctx, SSL3_VERSION);
+}
+static void ssl_set_TLSv10_func(SSL_CTX *ctx, int is_max) {
+       is_max ? SSL_CTX_set_max_proto_version(ctx, TLS1_VERSION)
+               : SSL_CTX_set_min_proto_version(ctx, TLS1_VERSION);
+}
+static void ssl_set_TLSv11_func(SSL_CTX *ctx, int is_max) {
+       is_max ? SSL_CTX_set_max_proto_version(ctx, TLS1_1_VERSION)
+               : SSL_CTX_set_min_proto_version(ctx, TLS1_1_VERSION);
+}
+static void ssl_set_TLSv12_func(SSL_CTX *ctx, int is_max) {
+       is_max ? SSL_CTX_set_max_proto_version(ctx, TLS1_2_VERSION)
+               : SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
+}
+#endif
+static void ssl_set_None_func(SSL_CTX *ctx, int i) {
+}
+
+static struct {
+       int      option;
+       uint16_t flag;
+       void   (*set_version)(SSL_CTX *, int);
+       const char *name;
+} methodVersions[] = {
+       {0, 0, ssl_set_None_func, "NONE"},   /* CONF_TLSV_NONE */
+       {SSL_OP_NO_SSLv3,   MC_SSL_O_NO_SSLV3,  ssl_set_SSLv3_func, "SSLv3"},    /* CONF_SSLV3 */
+       {SSL_OP_NO_TLSv1,   MC_SSL_O_NO_TLSV10, ssl_set_TLSv10_func, "TLSv1.0"}, /* CONF_TLSV10 */
+       {SSL_OP_NO_TLSv1_1, MC_SSL_O_NO_TLSV11, ssl_set_TLSv11_func, "TLSv1.1"}, /* CONF_TLSV11 */
+       {SSL_OP_NO_TLSv1_2, MC_SSL_O_NO_TLSV12, ssl_set_TLSv12_func, "TLSv1.2"}, /* CONF_TLSV12 */
+};
 
 /* Create an initial CTX used to start the SSL connection before switchctx */
 static SSL_CTX *
 ssl_sock_initial_ctx(struct bind_conf *bind_conf)
 {
        SSL_CTX *ctx = NULL;
-       long ssloptions =
+       long options =
                SSL_OP_ALL | /* all known workarounds for bugs */
                SSL_OP_NO_SSLv2 |
                SSL_OP_NO_COMPRESSION |
@@ -3181,67 +3265,43 @@ ssl_sock_initial_ctx(struct bind_conf *bind_conf)
                SSL_OP_SINGLE_ECDH_USE |
                SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION |
                SSL_OP_CIPHER_SERVER_PREFERENCE;
-       long sslmode =
+       long mode =
                SSL_MODE_ENABLE_PARTIAL_WRITE |
                SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER |
                SSL_MODE_RELEASE_BUFFERS |
                SSL_MODE_SMALL_BUFFERS;
-       int conf_ssl_options = bind_conf->ssl_options;
+       struct tls_version_filter *conf_ssl_methods = &bind_conf->ssl_methods;
+       int i, min, max;
 
-#if (OPENSSL_VERSION_NUMBER >= 0x1010000fL || defined OPENSSL_IS_BORINGSSL)
-       if (!ctx && conf_ssl_options & BC_SSL_O_USE_TLSV12) {
-               ctx = SSL_CTX_new(TLS_server_method());
-               SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
-               SSL_CTX_set_max_proto_version(ctx, TLS1_2_VERSION);
-       }
-       if (!ctx && conf_ssl_options & BC_SSL_O_USE_TLSV11) {
-               ctx = SSL_CTX_new(TLS_server_method());
-               SSL_CTX_set_min_proto_version(ctx, TLS1_1_VERSION);
-               SSL_CTX_set_max_proto_version(ctx, TLS1_1_VERSION);
-       }
-       if (!ctx && conf_ssl_options & BC_SSL_O_USE_TLSV10) {
-               ctx = SSL_CTX_new(TLS_server_method());
-               SSL_CTX_set_min_proto_version(ctx, TLS1_VERSION);
-               SSL_CTX_set_max_proto_version(ctx, TLS1_VERSION);
-       }
-       if (!ctx && conf_ssl_options & BC_SSL_O_USE_SSLV3) {
-               ctx = SSL_CTX_new(TLS_server_method());
-               SSL_CTX_set_min_proto_version(ctx, SSL3_VERSION);
-               SSL_CTX_set_max_proto_version(ctx, SSL3_VERSION);
-       }
-#else
-#if SSL_OP_NO_TLSv1_2
-       if (!ctx && conf_ssl_options & BC_SSL_O_USE_TLSV12)
-               ctx = SSL_CTX_new(TLSv1_2_server_method());
-#endif
-#if SSL_OP_NO_TLSv1_1
-       if (!ctx && conf_ssl_options & BC_SSL_O_USE_TLSV11)
-               ctx = SSL_CTX_new(TLSv1_1_server_method());
-#endif
-       if (!ctx && conf_ssl_options & BC_SSL_O_USE_TLSV10)
-               ctx = SSL_CTX_new(TLSv1_server_method());
-#ifndef OPENSSL_NO_SSL3
-       if (!ctx && conf_ssl_options & BC_SSL_O_USE_SSLV3)
-               ctx = SSL_CTX_new(SSLv3_server_method());
-#endif
+       ctx = SSL_CTX_new(SSLv23_server_method());
+
+       /* set options per default */
+       /* XXX need check hole */
+       for (i = CONF_TLSV_MIN; i <= CONF_TLSV_MAX; i++)
+               if (conf_ssl_methods->flags & methodVersions[i].flag)
+                       options |= methodVersions[i].option;
+
+       /* XXX min and max can be CONF_TLSV_NONE or unavailable in openssl.
+          Real min and max should be determinate with conf + openssl's capabilities
+        */
+       min = conf_ssl_methods->min;
+       max = conf_ssl_methods->max;
+#if (OPENSSL_VERSION_NUMBER < 0x1010000fL) && !defined(OPENSSL_IS_BORINGSSL)
+       /* Keep force-xxx implementation as it is in older haproxy. It's a
+          precautionary measure to avoid any suprise with older openssl version. */
+       if (min == max)
+               methodVersions[min].set_version(ctx, 1 /* server */);
+#else   /* openssl >= 1.1.0 */
+        methodVersions[min].set_version(ctx, 0);
+        methodVersions[max].set_version(ctx, 1);
 #endif
-       if (!ctx) {
-               ctx = SSL_CTX_new(SSLv23_server_method());
-               if (conf_ssl_options & BC_SSL_O_NO_SSLV3)
-                       ssloptions |= SSL_OP_NO_SSLv3;
-               if (conf_ssl_options & BC_SSL_O_NO_TLSV10)
-                       ssloptions |= SSL_OP_NO_TLSv1;
-               if (conf_ssl_options & BC_SSL_O_NO_TLSV11)
-                       ssloptions |= SSL_OP_NO_TLSv1_1;
-               if (conf_ssl_options & BC_SSL_O_NO_TLSV12)
-                       ssloptions |= SSL_OP_NO_TLSv1_2;
-       }
-       if (conf_ssl_options & BC_SSL_O_NO_TLS_TICKETS)
-               ssloptions |= SSL_OP_NO_TICKET;
-       if (conf_ssl_options & BC_SSL_O_PREF_CLIE_CIPH)
-               ssloptions &= ~SSL_OP_CIPHER_SERVER_PREFERENCE;
-       SSL_CTX_set_options(ctx, ssloptions);
-       SSL_CTX_set_mode(ctx, sslmode);
+
+       if (bind_conf->ssl_options & BC_SSL_O_NO_TLS_TICKETS)
+               options |= SSL_OP_NO_TICKET;
+       if (bind_conf->ssl_options & BC_SSL_O_PREF_CLIE_CIPH)
+               options &= ~SSL_OP_CIPHER_SERVER_PREFERENCE;
+       SSL_CTX_set_options(ctx, options);
+       SSL_CTX_set_mode(ctx, mode);
        if (global_ssl.life_time)
                SSL_CTX_set_timeout(ctx, global_ssl.life_time);
 
@@ -3596,6 +3656,8 @@ int ssl_sock_prepare_srv_ctx(struct server *srv)
                SSL_MODE_SMALL_BUFFERS;
        int verify = SSL_VERIFY_NONE;
        SSL_CTX *ctx = NULL;
+       struct tls_version_filter *conf_ssl_methods = &srv->ssl_ctx.methods;
+       int i, min, max;
 
        /* Make sure openssl opens /dev/urandom before the chroot */
        if (!ssl_initialize_random()) {
@@ -3613,56 +3675,7 @@ int ssl_sock_prepare_srv_ctx(struct server *srv)
        if (srv->check.use_ssl)
                srv->check.xprt = &ssl_sock;
 
-#if (OPENSSL_VERSION_NUMBER >= 0x1010000fL || defined OPENSSL_IS_BORINGSSL)
-       if (!ctx && srv->ssl_ctx.options & SRV_SSL_O_USE_TLSV12) {
-               ctx = SSL_CTX_new(TLS_client_method());
-               SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
-               SSL_CTX_set_max_proto_version(ctx, TLS1_2_VERSION);
-       }
-       if (!ctx && srv->ssl_ctx.options & SRV_SSL_O_USE_TLSV11) {
-               ctx = SSL_CTX_new(TLS_client_method());
-               SSL_CTX_set_min_proto_version(ctx, TLS1_1_VERSION);
-               SSL_CTX_set_max_proto_version(ctx, TLS1_1_VERSION);
-       }
-       if (!ctx && srv->ssl_ctx.options & SRV_SSL_O_USE_TLSV10) {
-               ctx = SSL_CTX_new(TLS_client_method());
-               SSL_CTX_set_min_proto_version(ctx, TLS1_VERSION);
-               SSL_CTX_set_max_proto_version(ctx, TLS1_VERSION);
-       }
-       if (!ctx && srv->ssl_ctx.options & SRV_SSL_O_USE_SSLV3) {
-               ctx = SSL_CTX_new(TLS_client_method());
-               SSL_CTX_set_min_proto_version(ctx, SSL3_VERSION);
-               SSL_CTX_set_max_proto_version(ctx, SSL3_VERSION);
-       }
-#else
-#if SSL_OP_NO_TLSv1_2
-       if (!ctx && srv->ssl_ctx.options & SRV_SSL_O_USE_TLSV12)
-               ctx = SSL_CTX_new(TLSv1_2_client_method());
-#endif
-#if SSL_OP_NO_TLSv1_1
-       if (!ctx && srv->ssl_ctx.options & SRV_SSL_O_USE_TLSV11)
-               ctx = SSL_CTX_new(TLSv1_1_client_method());
-#endif
-       if (!ctx && srv->ssl_ctx.options & SRV_SSL_O_USE_TLSV10)
-               ctx = SSL_CTX_new(TLSv1_client_method());
-#ifndef OPENSSL_NO_SSL3
-       if (!ctx && srv->ssl_ctx.options & SRV_SSL_O_USE_SSLV3)
-               ctx = SSL_CTX_new(SSLv3_client_method());
-#endif
-#endif
-       if (!ctx) {
-               ctx = SSL_CTX_new(SSLv23_client_method());
-               if (srv->ssl_ctx.options & SRV_SSL_O_NO_SSLV3)
-                       options |= SSL_OP_NO_SSLv3;
-               if (srv->ssl_ctx.options & SRV_SSL_O_NO_TLSV10)
-                       options |= SSL_OP_NO_TLSv1;
-               if (srv->ssl_ctx.options & SRV_SSL_O_NO_TLSV11)
-                       options |= SSL_OP_NO_TLSv1_1;
-               if (srv->ssl_ctx.options & SRV_SSL_O_NO_TLSV12)
-                       options |= SSL_OP_NO_TLSv1_2;
-       }
-       if (srv->ssl_ctx.options & SRV_SSL_O_NO_TLS_TICKETS)
-               options |= SSL_OP_NO_TICKET;
+       ctx = SSL_CTX_new(SSLv23_client_method());
        if (!ctx) {
                Alert("config : %s '%s', server '%s': unable to allocate ssl context.\n",
                      proxy_type_str(curproxy), curproxy->id,
@@ -3670,6 +3683,30 @@ int ssl_sock_prepare_srv_ctx(struct server *srv)
                cfgerr++;
                return cfgerr;
        }
+
+       /* set options per default */
+       /* XXX need check hole */
+       for (i = CONF_TLSV_MIN; i <= CONF_TLSV_MAX; i++)
+               if (conf_ssl_methods->flags & methodVersions[i].flag)
+                       options |= methodVersions[i].option;
+
+       /* XXX min and max can be CONF_TLSV_NONE or unavailable in openssl.
+          Real min and max should be determinate with conf + openssl's capabilities
+        */
+       min = conf_ssl_methods->min;
+       max = conf_ssl_methods->max;
+#if (OPENSSL_VERSION_NUMBER < 0x1010000fL) && !defined(OPENSSL_IS_BORINGSSL)
+       /* Keep force-xxx implementation as it is in older haproxy. It's a
+          precautionary measure to avoid any suprise with older openssl version. */
+       if (min == max)
+               methodVersions[min].set_version(ctx, 0 /* client */);
+#else   /* openssl >= 1.1.0 */
+        methodVersions[min].set_version(ctx, 0);
+        methodVersions[max].set_version(ctx, 1);
+#endif
+
+       if (srv->ssl_ctx.options & SRV_SSL_O_NO_TLS_TICKETS)
+               options |= SSL_OP_NO_TICKET;
        SSL_CTX_set_options(ctx, options);
        SSL_CTX_set_mode(ctx, mode);
        srv->ssl_ctx.ctx = ctx;
@@ -6123,50 +6160,52 @@ static int bind_parse_ignore_err(char **args, int cur_arg, struct proxy *px, str
        return 0;
 }
 
-/* parse the "force-sslv3" bind keyword */
-static int bind_parse_force_sslv3(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+/* parse tls_method_options */
+static int parse_tls_method_options(char *arg, struct tls_version_filter *methods)
 {
-#ifndef OPENSSL_NO_SSL3
-       conf->ssl_options |= BC_SSL_O_USE_SSLV3;
-       return 0;
-#else
-       if (err)
-               memprintf(err, "'%s' : library does not support protocol SSLv3", args[cur_arg]);
-       return ERR_ALERT | ERR_FATAL;
-#endif
-}
-
-/* parse the "force-tlsv10" bind keyword */
-static int bind_parse_force_tlsv10(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
-{
-       conf->ssl_options |= BC_SSL_O_USE_TLSV10;
+       char *p;
+       int v;
+       p = strchr(arg, '-');
+       if (!p)
+               return 1;
+       p++;
+       if (!strcmp(p, "sslv3"))
+               v = CONF_SSLV3;
+       else if (!strcmp(p, "tlsv10"))
+               v = CONF_TLSV10;
+       else if (!strcmp(p, "tlsv11"))
+               v = CONF_TLSV11;
+       else if (!strcmp(p, "tlsv12"))
+               v = CONF_TLSV12;
+       else
+               return 1;
+       if (!strncmp(arg, "no-", 3))
+               methods->flags |= methodVersions[v].flag;
+       else if (!strncmp(arg, "force-", 6))
+               methods->min = methods->max = v;
+       else
+               return 1;
        return 0;
 }
 
-/* parse the "force-tlsv11" bind keyword */
-static int bind_parse_force_tlsv11(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+static int bind_parse_tls_method_options(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
 {
-#if SSL_OP_NO_TLSv1_1
-       conf->ssl_options |= BC_SSL_O_USE_TLSV11;
+       if (parse_tls_method_options(args[cur_arg], &conf->ssl_methods)) {
+               if (err)
+                       memprintf(err, "'%s' : option not implemented", args[cur_arg]);
+               return ERR_ALERT | ERR_FATAL;
+       }
        return 0;
-#else
-       if (err)
-               memprintf(err, "'%s' : library does not support protocol TLSv1.1", args[cur_arg]);
-       return ERR_ALERT | ERR_FATAL;
-#endif
 }
 
-/* parse the "force-tlsv12" bind keyword */
-static int bind_parse_force_tlsv12(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+static int srv_parse_tls_method_options(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err)
 {
-#if SSL_OP_NO_TLSv1_2
-       conf->ssl_options |= BC_SSL_O_USE_TLSV12;
+       if (parse_tls_method_options(args[*cur_arg], &newsrv->ssl_ctx.methods)) {
+               if (err)
+                       memprintf(err, "'%s' : option not implemented", args[*cur_arg]);
+               return ERR_ALERT | ERR_FATAL;
+       }
        return 0;
-#else
-       if (err)
-               memprintf(err, "'%s' : library does not support protocol TLSv1.2", args[cur_arg]);
-       return ERR_ALERT | ERR_FATAL;
-#endif
 }
 
 /* parse the "no-tls-tickets" bind keyword */
@@ -6176,34 +6215,6 @@ static int bind_parse_no_tls_tickets(char **args, int cur_arg, struct proxy *px,
        return 0;
 }
 
-/* parse the "no-sslv3" bind keyword */
-static int bind_parse_no_sslv3(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
-{
-       conf->ssl_options |= BC_SSL_O_NO_SSLV3;
-       return 0;
-}
-
-/* parse the "no-tlsv10" bind keyword */
-static int bind_parse_no_tlsv10(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
-{
-       conf->ssl_options |= BC_SSL_O_NO_TLSV10;
-       return 0;
-}
-
-/* parse the "no-tlsv11" bind keyword */
-static int bind_parse_no_tlsv11(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
-{
-       conf->ssl_options |= BC_SSL_O_NO_TLSV11;
-       return 0;
-}
-
-/* parse the "no-tlsv12" bind keyword */
-static int bind_parse_no_tlsv12(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
-{
-       conf->ssl_options |= BC_SSL_O_NO_TLSV12;
-       return 0;
-}
-
 /* parse the "npn" bind keyword */
 static int ssl_bind_parse_npn(char **args, int cur_arg, struct proxy *px, struct ssl_bind_conf *conf, char **err)
 {
@@ -6325,6 +6336,11 @@ static int bind_parse_ssl(char **args, int cur_arg, struct proxy *px, struct bin
        if (global_ssl.listen_default_ciphers && !conf->ssl_conf.ciphers)
                conf->ssl_conf.ciphers = strdup(global_ssl.listen_default_ciphers);
        conf->ssl_options |= global_ssl.listen_default_ssloptions;
+       conf->ssl_methods.flags |= global_ssl.listen_default_sslmethods.flags;
+       if (!conf->ssl_methods.min)
+               conf->ssl_methods.min = global_ssl.listen_default_sslmethods.min;
+       if (!conf->ssl_methods.max)
+               conf->ssl_methods.max = global_ssl.listen_default_sslmethods.max;
 
        return 0;
 }
@@ -6485,6 +6501,12 @@ static int srv_parse_check_ssl(char **args, int *cur_arg, struct proxy *px, stru
        if (global_ssl.connect_default_ciphers && !newsrv->ssl_ctx.ciphers)
                newsrv->ssl_ctx.ciphers = strdup(global_ssl.connect_default_ciphers);
        newsrv->ssl_ctx.options |= global_ssl.connect_default_ssloptions;
+       newsrv->ssl_ctx.methods.flags |= global_ssl.connect_default_sslmethods.flags;
+       if (!newsrv->ssl_ctx.methods.min)
+               newsrv->ssl_ctx.methods.min = global_ssl.connect_default_sslmethods.min;
+       if (!newsrv->ssl_ctx.methods.max)
+               newsrv->ssl_ctx.methods.max = global_ssl.connect_default_sslmethods.max;
+
        return 0;
 }
 
@@ -6541,52 +6563,6 @@ static int srv_parse_crt(char **args, int *cur_arg, struct proxy *px, struct ser
        return 0;
 }
 
-/* parse the "force-sslv3" server keyword */
-static int srv_parse_force_sslv3(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err)
-{
-#ifndef OPENSSL_NO_SSL3
-       newsrv->ssl_ctx.options |= SRV_SSL_O_USE_SSLV3;
-       return 0;
-#else
-       if (err)
-               memprintf(err, "'%s' : library does not support protocol SSLv3", args[*cur_arg]);
-       return ERR_ALERT | ERR_FATAL;
-#endif
-}
-
-/* parse the "force-tlsv10" server keyword */
-static int srv_parse_force_tlsv10(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err)
-{
-       newsrv->ssl_ctx.options |= SRV_SSL_O_USE_TLSV10;
-       return 0;
-}
-
-/* parse the "force-tlsv11" server keyword */
-static int srv_parse_force_tlsv11(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err)
-{
-#if SSL_OP_NO_TLSv1_1
-       newsrv->ssl_ctx.options |= SRV_SSL_O_USE_TLSV11;
-       return 0;
-#else
-       if (err)
-               memprintf(err, "'%s' : library does not support protocol TLSv1.1", args[*cur_arg]);
-       return ERR_ALERT | ERR_FATAL;
-#endif
-}
-
-/* parse the "force-tlsv12" server keyword */
-static int srv_parse_force_tlsv12(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err)
-{
-#if SSL_OP_NO_TLSv1_2
-       newsrv->ssl_ctx.options |= SRV_SSL_O_USE_TLSV12;
-       return 0;
-#else
-       if (err)
-               memprintf(err, "'%s' : library does not support protocol TLSv1.2", args[*cur_arg]);
-       return ERR_ALERT | ERR_FATAL;
-#endif
-}
-
 /* parse the "no-check-ssl" server keyword */
 static int srv_parse_no_check_ssl(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err)
 {
@@ -6630,34 +6606,6 @@ static int srv_parse_no_ssl_reuse(char **args, int *cur_arg, struct proxy *px, s
        return 0;
 }
 
-/* parse the "no-sslv3" server keyword */
-static int srv_parse_no_sslv3(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err)
-{
-       newsrv->ssl_ctx.options |= SRV_SSL_O_NO_SSLV3;
-       return 0;
-}
-
-/* parse the "no-tlsv10" server keyword */
-static int srv_parse_no_tlsv10(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err)
-{
-       newsrv->ssl_ctx.options |= SRV_SSL_O_NO_TLSV10;
-       return 0;
-}
-
-/* parse the "no-tlsv11" server keyword */
-static int srv_parse_no_tlsv11(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err)
-{
-       newsrv->ssl_ctx.options |= SRV_SSL_O_NO_TLSV11;
-       return 0;
-}
-
-/* parse the "no-tlsv12" server keyword */
-static int srv_parse_no_tlsv12(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err)
-{
-       newsrv->ssl_ctx.options |= SRV_SSL_O_NO_TLSV12;
-       return 0;
-}
-
 /* parse the "no-tls-tickets" server keyword */
 static int srv_parse_no_tls_tickets(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err)
 {
@@ -6775,39 +6723,11 @@ static int ssl_parse_default_bind_options(char **args, int section_type, struct
                return -1;
        }
        while (*(args[i])) {
-               if (!strcmp(args[i], "no-sslv3"))
-                       global_ssl.listen_default_ssloptions |= BC_SSL_O_NO_SSLV3;
-               else if (!strcmp(args[i], "no-tlsv10"))
-                       global_ssl.listen_default_ssloptions |= BC_SSL_O_NO_TLSV10;
-               else if (!strcmp(args[i], "no-tlsv11"))
-                       global_ssl.listen_default_ssloptions |= BC_SSL_O_NO_TLSV11;
-               else if (!strcmp(args[i], "no-tlsv12"))
-                       global_ssl.listen_default_ssloptions |= BC_SSL_O_NO_TLSV12;
-               else if (!strcmp(args[i], "force-sslv3"))
-                       global_ssl.listen_default_ssloptions |= BC_SSL_O_USE_SSLV3;
-               else if (!strcmp(args[i], "force-tlsv10"))
-                       global_ssl.listen_default_ssloptions |= BC_SSL_O_USE_TLSV10;
-               else if (!strcmp(args[i], "force-tlsv11")) {
-#if SSL_OP_NO_TLSv1_1
-                       global_ssl.listen_default_ssloptions |= BC_SSL_O_USE_TLSV11;
-#else
-                       memprintf(err, "'%s' '%s': library does not support protocol TLSv1.1", args[0], args[i]);
-                       return -1;
-#endif
-               }
-               else if (!strcmp(args[i], "force-tlsv12")) {
-#if SSL_OP_NO_TLSv1_2
-                       global_ssl.listen_default_ssloptions |= BC_SSL_O_USE_TLSV12;
-#else
-                       memprintf(err, "'%s' '%s': library does not support protocol TLSv1.2", args[0], args[i]);
-                       return -1;
-#endif
-               }
-               else if (!strcmp(args[i], "no-tls-tickets"))
+               if (!strcmp(args[i], "no-tls-tickets"))
                        global_ssl.listen_default_ssloptions |= BC_SSL_O_NO_TLS_TICKETS;
                else if (!strcmp(args[i], "prefer-client-ciphers"))
                        global_ssl.listen_default_ssloptions |= BC_SSL_O_PREF_CLIE_CIPH;
-               else {
+               else if (parse_tls_method_options(args[i], &global_ssl.listen_default_sslmethods)) {
                        memprintf(err, "unknown option '%s' on global statement '%s'.", args[i], args[0]);
                        return -1;
                }
@@ -6827,37 +6747,9 @@ static int ssl_parse_default_server_options(char **args, int section_type, struc
                return -1;
        }
        while (*(args[i])) {
-               if (!strcmp(args[i], "no-sslv3"))
-                       global_ssl.connect_default_ssloptions |= SRV_SSL_O_NO_SSLV3;
-               else if (!strcmp(args[i], "no-tlsv10"))
-                       global_ssl.connect_default_ssloptions |= SRV_SSL_O_NO_TLSV10;
-               else if (!strcmp(args[i], "no-tlsv11"))
-                       global_ssl.connect_default_ssloptions |= SRV_SSL_O_NO_TLSV11;
-               else if (!strcmp(args[i], "no-tlsv12"))
-                       global_ssl.connect_default_ssloptions |= SRV_SSL_O_NO_TLSV12;
-               else if (!strcmp(args[i], "force-sslv3"))
-                       global_ssl.connect_default_ssloptions |= SRV_SSL_O_USE_SSLV3;
-               else if (!strcmp(args[i], "force-tlsv10"))
-                       global_ssl.connect_default_ssloptions |= SRV_SSL_O_USE_TLSV10;
-               else if (!strcmp(args[i], "force-tlsv11")) {
-#if SSL_OP_NO_TLSv1_1
-                       global_ssl.connect_default_ssloptions |= SRV_SSL_O_USE_TLSV11;
-#else
-                       memprintf(err, "'%s' '%s': library does not support protocol TLSv1.1", args[0], args[i]);
-                       return -1;
-#endif
-               }
-               else if (!strcmp(args[i], "force-tlsv12")) {
-#if SSL_OP_NO_TLSv1_2
-                       global_ssl.connect_default_ssloptions |= SRV_SSL_O_USE_TLSV12;
-#else
-                       memprintf(err, "'%s' '%s': library does not support protocol TLSv1.2", args[0], args[i]);
-                       return -1;
-#endif
-               }
-               else if (!strcmp(args[i], "no-tls-tickets"))
+               if (!strcmp(args[i], "no-tls-tickets"))
                        global_ssl.connect_default_ssloptions |= SRV_SSL_O_NO_TLS_TICKETS;
-               else {
+               else if (parse_tls_method_options(args[i], &global_ssl.connect_default_sslmethods)) {
                        memprintf(err, "unknown option '%s' on global statement '%s'.", args[i], args[0]);
                        return -1;
                }
@@ -7415,34 +7307,34 @@ static struct ssl_bind_kw ssl_bind_kws[] = {
 };
 
 static struct bind_kw_list bind_kws = { "SSL", { }, {
-       { "alpn",                  bind_parse_alpn,            1 }, /* set ALPN supported protocols */
-       { "ca-file",               bind_parse_ca_file,         1 }, /* set CAfile to process verify on client cert */
-       { "ca-ignore-err",         bind_parse_ignore_err,      1 }, /* set error IDs to ignore on verify depth > 0 */
-       { "ca-sign-file",          bind_parse_ca_sign_file,    1 }, /* set CAFile used to generate and sign server certs */
-       { "ca-sign-pass",          bind_parse_ca_sign_pass,    1 }, /* set CAKey passphrase */
-       { "ciphers",               bind_parse_ciphers,         1 }, /* set SSL cipher suite */
-       { "crl-file",              bind_parse_crl_file,        1 }, /* set certificat revocation list file use on client cert verify */
-       { "crt",                   bind_parse_crt,             1 }, /* load SSL certificates from this location */
-       { "crt-ignore-err",        bind_parse_ignore_err,      1 }, /* set error IDs to ingore on verify depth == 0 */
-       { "crt-list",              bind_parse_crt_list,        1 }, /* load a list of crt from this location */
-       { "curves",                bind_parse_curves,          1 }, /* set SSL curve suite */
-       { "ecdhe",                 bind_parse_ecdhe,           1 }, /* defines named curve for elliptic curve Diffie-Hellman */
-       { "force-sslv3",           bind_parse_force_sslv3,     0 }, /* force SSLv3 */
-       { "force-tlsv10",          bind_parse_force_tlsv10,    0 }, /* force TLSv10 */
-       { "force-tlsv11",          bind_parse_force_tlsv11,    0 }, /* force TLSv11 */
-       { "force-tlsv12",          bind_parse_force_tlsv12,    0 }, /* force TLSv12 */
-       { "generate-certificates", bind_parse_generate_certs,  0 }, /* enable the server certificates generation */
-       { "no-sslv3",              bind_parse_no_sslv3,        0 }, /* disable SSLv3 */
-       { "no-tlsv10",             bind_parse_no_tlsv10,       0 }, /* disable TLSv10 */
-       { "no-tlsv11",             bind_parse_no_tlsv11,       0 }, /* disable TLSv11 */
-       { "no-tlsv12",             bind_parse_no_tlsv12,       0 }, /* disable TLSv12 */
-       { "no-tls-tickets",        bind_parse_no_tls_tickets,  0 }, /* disable session resumption tickets */
-       { "ssl",                   bind_parse_ssl,             0 }, /* enable SSL processing */
-       { "strict-sni",            bind_parse_strict_sni,      0 }, /* refuse negotiation if sni doesn't match a certificate */
-       { "tls-ticket-keys",       bind_parse_tls_ticket_keys, 1 }, /* set file to load TLS ticket keys from */
-       { "verify",                bind_parse_verify,          1 }, /* set SSL verify method */
-       { "npn",                   bind_parse_npn,             1 }, /* set NPN supported protocols */
-       { "prefer-client-ciphers", bind_parse_pcc,             0 }, /* prefer client ciphers */
+       { "alpn",                  bind_parse_alpn,               1 }, /* set ALPN supported protocols */
+       { "ca-file",               bind_parse_ca_file,            1 }, /* set CAfile to process verify on client cert */
+       { "ca-ignore-err",         bind_parse_ignore_err,         1 }, /* set error IDs to ignore on verify depth > 0 */
+       { "ca-sign-file",          bind_parse_ca_sign_file,       1 }, /* set CAFile used to generate and sign server certs */
+       { "ca-sign-pass",          bind_parse_ca_sign_pass,       1 }, /* set CAKey passphrase */
+       { "ciphers",               bind_parse_ciphers,            1 }, /* set SSL cipher suite */
+       { "crl-file",              bind_parse_crl_file,           1 }, /* set certificat revocation list file use on client cert verify */
+       { "crt",                   bind_parse_crt,                1 }, /* load SSL certificates from this location */
+       { "crt-ignore-err",        bind_parse_ignore_err,         1 }, /* set error IDs to ingore on verify depth == 0 */
+       { "crt-list",              bind_parse_crt_list,           1 }, /* load a list of crt from this location */
+       { "curves",                bind_parse_curves,             1 }, /* set SSL curve suite */
+       { "ecdhe",                 bind_parse_ecdhe,              1 }, /* defines named curve for elliptic curve Diffie-Hellman */
+       { "force-sslv3",           bind_parse_tls_method_options, 0 }, /* force SSLv3 */
+       { "force-tlsv10",          bind_parse_tls_method_options, 0 }, /* force TLSv10 */
+       { "force-tlsv11",          bind_parse_tls_method_options, 0 }, /* force TLSv11 */
+       { "force-tlsv12",          bind_parse_tls_method_options, 0 }, /* force TLSv12 */
+       { "generate-certificates", bind_parse_generate_certs,     0 }, /* enable the server certificates generation */
+       { "no-sslv3",              bind_parse_tls_method_options, 0 }, /* disable SSLv3 */
+       { "no-tlsv10",             bind_parse_tls_method_options, 0 }, /* disable TLSv10 */
+       { "no-tlsv11",             bind_parse_tls_method_options, 0 }, /* disable TLSv11 */
+       { "no-tlsv12",             bind_parse_tls_method_options, 0 }, /* disable TLSv12 */
+       { "no-tls-tickets",        bind_parse_no_tls_tickets,     0 }, /* disable session resumption tickets */
+       { "ssl",                   bind_parse_ssl,                0 }, /* enable SSL processing */
+       { "strict-sni",            bind_parse_strict_sni,         0 }, /* refuse negotiation if sni doesn't match a certificate */
+       { "tls-ticket-keys",       bind_parse_tls_ticket_keys,    1 }, /* set file to load TLS ticket keys from */
+       { "verify",                bind_parse_verify,             1 }, /* set SSL verify method */
+       { "npn",                   bind_parse_npn,                1 }, /* set NPN supported protocols */
+       { "prefer-client-ciphers", bind_parse_pcc,                0 }, /* prefer client ciphers */
        { NULL, NULL, 0 },
 }};
 
@@ -7459,19 +7351,19 @@ static struct srv_kw_list srv_kws = { "SSL", { }, {
        { "ciphers",                 srv_parse_ciphers,           1, 1 }, /* select the cipher suite */
        { "crl-file",                srv_parse_crl_file,          1, 1 }, /* set certificate revocation list file use on server cert verify */
        { "crt",                     srv_parse_crt,               1, 1 }, /* set client certificate */
-       { "force-sslv3",             srv_parse_force_sslv3,       0, 1 }, /* force SSLv3 */
-       { "force-tlsv10",            srv_parse_force_tlsv10,      0, 1 }, /* force TLSv10 */
-       { "force-tlsv11",            srv_parse_force_tlsv11,      0, 1 }, /* force TLSv11 */
-       { "force-tlsv12",            srv_parse_force_tlsv12,      0, 1 }, /* force TLSv12 */
+       { "force-sslv3",             srv_parse_tls_method_options,0, 1 }, /* force SSLv3 */
+       { "force-tlsv10",            srv_parse_tls_method_options,0, 1 }, /* force TLSv10 */
+       { "force-tlsv11",            srv_parse_tls_method_options,0, 1 }, /* force TLSv11 */
+       { "force-tlsv12",            srv_parse_tls_method_options,0, 1 }, /* force TLSv12 */
        { "no-check-ssl",            srv_parse_no_check_ssl,      0, 1 }, /* disable SSL for health checks */
        { "no-send-proxy-v2-ssl",    srv_parse_no_send_proxy_ssl, 0, 1 }, /* do not send PROXY protocol header v2 with SSL info */
        { "no-send-proxy-v2-ssl-cn", srv_parse_no_send_proxy_cn,  0, 1 }, /* do not send PROXY protocol header v2 with CN */
        { "no-ssl",                  srv_parse_no_ssl,            0, 1 }, /* disable SSL processing */
        { "no-ssl-reuse",            srv_parse_no_ssl_reuse,      0, 1 }, /* disable session reuse */
-       { "no-sslv3",                srv_parse_no_sslv3,          0, 0 }, /* disable SSLv3 */
-       { "no-tlsv10",               srv_parse_no_tlsv10,         0, 0 }, /* disable TLSv10 */
-       { "no-tlsv11",               srv_parse_no_tlsv11,         0, 0 }, /* disable TLSv11 */
-       { "no-tlsv12",               srv_parse_no_tlsv12,         0, 0 }, /* disable TLSv12 */
+       { "no-sslv3",                srv_parse_tls_method_options,0, 0 }, /* disable SSLv3 */
+       { "no-tlsv10",               srv_parse_tls_method_options,0, 0 }, /* disable TLSv10 */
+       { "no-tlsv11",               srv_parse_tls_method_options,0, 0 }, /* disable TLSv11 */
+       { "no-tlsv12",               srv_parse_tls_method_options,0, 0 }, /* disable TLSv12 */
        { "no-tls-tickets",          srv_parse_no_tls_tickets,    0, 1 }, /* disable session resumption tickets */
        { "send-proxy-v2-ssl",       srv_parse_send_proxy_ssl,    0, 1 }, /* send PROXY protocol header v2 with SSL info */
        { "send-proxy-v2-ssl-cn",    srv_parse_send_proxy_cn,     0, 1 }, /* send PROXY protocol header v2 with CN */