From 501d9fdb86088b1d785a46be8382a3bbe6a3b905 Mon Sep 17 00:00:00 2001 From: William Lallemand Date: Tue, 12 Mar 2024 16:22:34 +0100 Subject: [PATCH] MEDIUM: ssl: allow to change the OpenSSL security level from global section The new "ssl-security-level" option allows one to change the OpenSSL security level without having to change the openssl.cnf global file of your distribution. This directives applies on every SSL_CTX context. People sometimes change their security level directly in the ciphers directive, however there are some cases when the security level change is not applied in the right order (for example when applying a DH param). Before this patch, it was to possible to trick by using a specific openssl.cnf file and start haproxy this way: OPENSSL_CONF=./openssl.cnf ./haproxy -f bug-2468.cfg Values for the security level can be found there: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_security_level.html This was discussed in github issue #2468. --- doc/configuration.txt | 12 +++++++++++ include/haproxy/openssl-compat.h | 15 ++++++++++++++ include/haproxy/ssl_sock-t.h | 1 + src/cfgparse-ssl.c | 34 ++++++++++++++++++++++++++++++++ src/quic_ssl.c | 2 ++ src/ssl_gencert.c | 4 ++++ src/ssl_sock.c | 12 +++++++++++ 7 files changed, 80 insertions(+) diff --git a/doc/configuration.txt b/doc/configuration.txt index e01e21960e..6f78d77238 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -1311,6 +1311,7 @@ The following keywords are supported in the "global" section : - ssl-propquery - ssl-provider - ssl-provider-path + - ssl-security-level - ssl-server-verify - ssl-skip-self-issued-ca - stats @@ -2588,6 +2589,17 @@ ssl-load-extra-files * See also: "crt", section 5.1 about bind options and section 5.2 about server options. +ssl-security-level + This directive allows to chose the OpenSSL security level as described in + https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_security_level.html + The security level will be applied to every SSL contextes in HAProxy. + Only a value between 0 and 5 is supported. + + The default value depends on your OpenSSL version, distribution and how was + compiled the library. + + This directive requires at least OpenSSL 1.1.1. + ssl-server-verify [none|required] The default behavior for SSL verify on servers side. If specified to 'none', servers certificates are not verified. The default is 'required' except if diff --git a/include/haproxy/openssl-compat.h b/include/haproxy/openssl-compat.h index 48160e8326..7cd21a8fd3 100644 --- a/include/haproxy/openssl-compat.h +++ b/include/haproxy/openssl-compat.h @@ -118,6 +118,16 @@ #define HAVE_SSL_0RTT_QUIC #endif + +#if defined(SSL_CTX_set_security_level) || HA_OPENSSL_VERSION_NUMBER >= 0x1010100fL +#define HAVE_SSL_SET_SECURITY_LEVEL +#endif + +#if !defined(HAVE_SSL_SET_SECURITY_LEVEL) +/* define a nope function for set_security_level */ +#define SSL_CTX_set_security_level(ctx, level) ({}) +#endif + #if (HA_OPENSSL_VERSION_NUMBER >= 0x3000000fL) #define HAVE_OSSL_PARAM #define MAC_CTX EVP_MAC_CTX @@ -309,6 +319,11 @@ static inline int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s) #endif + +#if (HA_OPENSSL_VERSION_NUMBER < 0x10101000L) +#endif + + #if (HA_OPENSSL_VERSION_NUMBER < 0x3000000fL) #if defined(SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB) #define SSL_CTX_set_tlsext_ticket_key_evp_cb SSL_CTX_set_tlsext_ticket_key_cb diff --git a/include/haproxy/ssl_sock-t.h b/include/haproxy/ssl_sock-t.h index fdf41a7a96..2d8f8157e1 100644 --- a/include/haproxy/ssl_sock-t.h +++ b/include/haproxy/ssl_sock-t.h @@ -303,6 +303,7 @@ struct global_ssl { int keylog; /* activate keylog */ int extra_files; /* which files not defined in the configuration file are we looking for */ int extra_files_noext; /* whether we remove the extension when looking up a extra file */ + int security_level; /* configure the openssl security level */ #ifndef OPENSSL_NO_OCSP struct { diff --git a/src/cfgparse-ssl.c b/src/cfgparse-ssl.c index 3f728258dd..23b515d23e 100644 --- a/src/cfgparse-ssl.c +++ b/src/cfgparse-ssl.c @@ -2120,6 +2120,39 @@ static int ssl_parse_global_ca_crt_base(char **args, int section_type, struct pr return 0; } +/* parse the "ssl-security-level" keyword in global section. */ +static int ssl_parse_security_level(char **args, int section_type, struct proxy *curpx, + const struct proxy *defpx, const char *file, int linenum, + char **err) +{ +#ifndef HAVE_SSL_SET_SECURITY_LEVEL + memprintf(err, "global statement '%s' requires at least OpenSSL 1.1.1.", args[0]); + return -1; +#else + char *endptr; + + if (!*args[1]) { + ha_alert("parsing [%s:%d] : '%s' : missing value\n", file, linenum, args[0]); + return -1; + } + + global_ssl.security_level = strtol(args[1], &endptr, 10); + if (*endptr != '\0') { + ha_alert("parsing [%s:%d] : '%s' : expects an integer argument, found '%s'\n", + file, linenum, args[0], args[1]); + return -1; + } + + if (global_ssl.security_level < 0 || global_ssl.security_level > 5) { + ha_alert("parsing [%s:%d] : '%s' : expects a value between 0 and 5\n", + file, linenum, args[0]); + return -1; + } +#endif + + return 0; +} + /* parse the "ssl-skip-self-issued-ca" keyword in global section. */ static int ssl_parse_skip_self_issued_ca(char **args, int section_type, struct proxy *curpx, const struct proxy *defpx, const char *file, int line, @@ -2343,6 +2376,7 @@ static struct cfg_kw_list cfg_kws = {ILH, { { CFG_GLOBAL, "ssl-provider", ssl_parse_global_ssl_provider }, { CFG_GLOBAL, "ssl-provider-path", ssl_parse_global_ssl_provider_path }, #endif + { CFG_GLOBAL, "ssl-security-level", ssl_parse_security_level }, { CFG_GLOBAL, "ssl-skip-self-issued-ca", ssl_parse_skip_self_issued_ca }, { CFG_GLOBAL, "tune.ssl.cachesize", ssl_parse_global_int }, #ifndef OPENSSL_NO_DH diff --git a/src/quic_ssl.c b/src/quic_ssl.c index d7f112d992..51a96c2a31 100644 --- a/src/quic_ssl.c +++ b/src/quic_ssl.c @@ -441,6 +441,8 @@ int ssl_quic_initial_ctx(struct bind_conf *bind_conf) ctx = SSL_CTX_new(TLS_server_method()); bind_conf->initial_ctx = ctx; + if (global_ssl.security_level > -1) + SSL_CTX_set_security_level(ctx, global_ssl.security_level); SSL_CTX_set_options(ctx, options); SSL_CTX_set_mode(ctx, SSL_MODE_RELEASE_BUFFERS); SSL_CTX_set_min_proto_version(ctx, TLS1_3_VERSION); diff --git a/src/ssl_gencert.c b/src/ssl_gencert.c index 95195d1338..44dc82c742 100644 --- a/src/ssl_gencert.c +++ b/src/ssl_gencert.c @@ -201,6 +201,10 @@ static SSL_CTX *ssl_sock_do_create_cert(const char *servername, struct bind_conf /* Create and set the new SSL_CTX */ if (!(ssl_ctx = SSL_CTX_new(SSLv23_server_method()))) goto mkcert_error; + + if (global_ssl.security_level > -1) + SSL_CTX_set_security_level(ssl_ctx, global_ssl.security_level); + if (!SSL_CTX_use_PrivateKey(ssl_ctx, pkey)) goto mkcert_error; if (!SSL_CTX_use_certificate(ssl_ctx, newcrt)) diff --git a/src/ssl_sock.c b/src/ssl_sock.c index bd2031c738..85998ae4ee 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -136,6 +136,7 @@ struct global_ssl global_ssl = { #ifdef HAVE_SSL_KEYLOG .keylog = 0, #endif + .security_level = -1, #ifndef OPENSSL_NO_OCSP .ocsp_update.delay_max = SSL_OCSP_UPDATE_DELAY_MAX, .ocsp_update.delay_min = SSL_OCSP_UPDATE_DELAY_MIN, @@ -3458,6 +3459,9 @@ int ckch_inst_new_load_store(const char *path, struct ckch_store *ckchs, struct goto error; } + if (global_ssl.security_level > -1) + SSL_CTX_set_security_level(ctx, global_ssl.security_level); + errcode |= ssl_sock_put_ckch_into_ctx(path, data, ctx, err); if (errcode & ERR_CODE) goto error; @@ -3612,6 +3616,9 @@ int ckch_inst_new_load_srv_store(const char *path, struct ckch_store *ckchs, goto error; } + if (global_ssl.security_level > -1) + SSL_CTX_set_security_level(ctx, global_ssl.security_level); + errcode |= ssl_sock_put_srv_ckch_into_ctx(path, data, ctx, err); if (errcode & ERR_CODE) goto error; @@ -3959,6 +3966,9 @@ ssl_sock_initial_ctx(struct bind_conf *bind_conf) ctx = SSL_CTX_new(SSLv23_server_method()); bind_conf->initial_ctx = ctx; + if (global_ssl.security_level > -1) + SSL_CTX_set_security_level(ctx, global_ssl.security_level); + if (conf_ssl_methods->flags && (conf_ssl_methods->min || conf_ssl_methods->max)) ha_warning("Proxy '%s': no-sslv3/no-tlsv1x are ignored for bind '%s' at [%s:%d]. " "Use only 'ssl-min-ver' and 'ssl-max-ver' to fix.\n", @@ -4955,6 +4965,8 @@ int ssl_sock_prepare_srv_ctx(struct server *srv) cfgerr++; return cfgerr; } + if (global_ssl.security_level > -1) + SSL_CTX_set_security_level(ctx, global_ssl.security_level); srv->ssl_ctx.ctx = ctx; }