From df701a2adbc57670bb6fffd4744f5deae23d11e4 Mon Sep 17 00:00:00 2001 From: Emmanuel Hocdet Date: Thu, 18 May 2017 12:46:50 +0200 Subject: [PATCH] MINOR: ssl: support ssl-min-ver and ssl-max-ver with crt-list SSL/TLS version can be changed per certificat if and only if openssl lib support earlier callback on handshake and, of course, is implemented in haproxy. It's ok for BoringSSL. For Openssl, version 1.1.1 have such callback and could support it. --- doc/configuration.txt | 3 ++- include/types/listener.h | 1 + src/ssl_sock.c | 50 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/doc/configuration.txt b/doc/configuration.txt index 969734c69..19c11323c 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -10415,7 +10415,8 @@ crt-list [\[ ...\]] [[!] ...] sslbindconf support "npn", "alpn", "verify", "ca_file", "crl_file", "ecdhe", - "curves", "ciphers" configuration. + "curves", "ciphers" configuration. With BoringSSL "ssl-min-ver" and + "ssl-max-ver" are also supported. It override the configuration set in bind line for the certificate. Wildcards are supported in the SNI filter. Negative filter are also supported, diff --git a/include/types/listener.h b/include/types/listener.h index 93f36624c..9a77e9654 100644 --- a/include/types/listener.h +++ b/include/types/listener.h @@ -124,6 +124,7 @@ struct ssl_bind_conf { char *ciphers; /* cipher suite to use if non-null */ char *curves; /* curves suite to use for ECDHE */ char *ecdhe; /* named curve to use for ECDHE */ + struct tls_version_filter ssl_methods; /* ssl methods */ #endif }; diff --git a/src/ssl_sock.c b/src/ssl_sock.c index 67fa2aebb..776140f49 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -2106,8 +2106,15 @@ static int ssl_sock_switchctx_cbk(const struct ssl_early_callback_ctx *ctx) node = node_ecdsa ? node_ecdsa : (node_rsa ? node_rsa : node_anonymous); if (node) { + int min, max; /* switch ctx */ ssl_sock_switchctx_set(ctx->ssl, container_of(node, struct sni_ctx, name)->ctx); + min = container_of(node, struct sni_ctx, name)->conf->ssl_methods.min; + if (min != s->ssl_methods.min) + methodVersions[min].ssl_set_version(ctx->ssl, SET_MIN); + max = container_of(node, struct sni_ctx, name)->conf->ssl_methods.max; + if (max != s->ssl_methods.max) + methodVersions[max].ssl_set_version(ctx->ssl, SET_MAX); return 1; } if (!s->strict_sni) { @@ -3572,6 +3579,9 @@ ssl_sock_initial_ctx(struct bind_conf *bind_conf) bind_conf->frontend->id, bind_conf->arg, bind_conf->file, bind_conf->line); cfgerr += 1; } + /* save real min/max in bind_conf */ + conf_ssl_methods->min = min; + conf_ssl_methods->max = max; #if (OPENSSL_VERSION_NUMBER < 0x1010000fL) && !defined(OPENSSL_IS_BORINGSSL) /* Keep force-xxx implementation as it is in older haproxy. It's a @@ -3623,6 +3633,36 @@ int ssl_sock_prepare_ctx(struct bind_conf *bind_conf, struct ssl_bind_conf *ssl_ const char *conf_ciphers; const char *conf_curves = NULL; + if (ssl_conf) { + struct tls_version_filter *conf_ssl_methods = &ssl_conf->ssl_methods; + int i, min, max; + int flags = MC_SSL_O_ALL; + + /* Real min and max should be determinate with configuration and openssl's capabilities */ + min = conf_ssl_methods->min ? conf_ssl_methods->min : bind_conf->ssl_methods.min; + max = conf_ssl_methods->max ? conf_ssl_methods->max : bind_conf->ssl_methods.max; + if (min) + flags |= (methodVersions[min].flag - 1); + if (max) + flags |= ~((methodVersions[max].flag << 1) - 1); + min = max = CONF_TLSV_NONE; + for (i = CONF_TLSV_MIN; i <= CONF_TLSV_MAX; i++) + if (methodVersions[i].option && !(flags & methodVersions[i].flag)) { + if (min) + max = i; + else + min = max = i; + } + /* save real min/max */ + conf_ssl_methods->min = min; + conf_ssl_methods->max = max; + if (!min) { + Alert("Proxy '%s': all SSL/TLS versions are disabled for bind '%s' at [%s:%d].\n", + bind_conf->frontend->id, bind_conf->arg, bind_conf->file, bind_conf->line); + cfgerr += 1; + } + } + switch ((ssl_conf && ssl_conf->verify) ? ssl_conf->verify : bind_conf->ssl_conf.verify) { case SSL_SOCK_VERIFY_NONE: verify = SSL_VERIFY_NONE; @@ -6641,6 +6681,14 @@ static int parse_tls_method_minmax(char **args, int cur_arg, struct tls_version_ return 0; } +static int ssl_bind_parse_tls_method_minmax(char **args, int cur_arg, struct proxy *px, struct ssl_bind_conf *conf, char **err) +{ +#if !defined(OPENSSL_IS_BORINGSSL) + Warning("crt-list: ssl-min-ver and ssl-max-ver are not supported with this Openssl version (skipped).\n"); +#endif + return parse_tls_method_minmax(args, cur_arg, &conf->ssl_methods, err); +} + static int bind_parse_tls_method_minmax(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err) { return parse_tls_method_minmax(args, cur_arg, &conf->ssl_methods, err); @@ -7831,6 +7879,8 @@ static struct ssl_bind_kw ssl_bind_kws[] = { { "curves", ssl_bind_parse_curves, 1 }, /* set SSL curve suite */ { "ecdhe", ssl_bind_parse_ecdhe, 1 }, /* defines named curve for elliptic curve Diffie-Hellman */ { "npn", ssl_bind_parse_npn, 1 }, /* set NPN supported protocols */ + { "ssl-min-ver", ssl_bind_parse_tls_method_minmax,1 }, /* minimum version */ + { "ssl-max-ver", ssl_bind_parse_tls_method_minmax,1 }, /* maximum version */ { "verify", ssl_bind_parse_verify, 1 }, /* set SSL verify method */ { NULL, NULL, 0 }, };