From 3095fa27abb08c94eee4312183af2b0bad81b6ee Mon Sep 17 00:00:00 2001 From: Frederic Lecaille Date: Tue, 25 Nov 2025 20:45:27 +0100 Subject: [PATCH] BUG/MEDIUM: quic: support some ciphersuites and curves related options This patch impacts both the QUIC frontends and listeners. Before this patch "ssl-default-bind-ciphersuites", "ssl-default-bind-curves", were ignored by QUIC. For the backend, "ssl-default-server-ciphersuites" and "ssl-default-server-curves" were not ignored but set from set from ssl_quic_srv_new_ssl_ctx() which is QUIC specific, in place of ssl_sock_init_srv() as this is done for TCP. Rename global variable to and to to reflect the OpenSSL API naming. On frontend side, add support for "ssl-default-bind-ciphersuites" and "ssl-default-bind-curves" global options and "ciphersuites" and "curves" "bind" options. These options are taken into an account by ssl_quic_initial_ctx() which inspects these four variable before calling SSL_CTX_set_ciphersuites() and SSL_CTX_set_curves(). Note that the bind_conf struct is not modified when no "ciphersuites" or "curves" option are used on "bind" lines. Idem on backend side, rely on ssl_sock_init_srv() to set the server ciphersuites and curves. This function is modified to use respectively and if no ciphersuites and curves were set by "ssl-default-server-ciphersuites", "ssl-default-server-curves" as global options or "ciphersuites", "curves" as "server" line options. Thank to @rwagoner for having reported this issue in GH #3194 when using an OpenSSL 3.5.4 stack with FIPS support. Must be backported as far as 2.6 --- include/haproxy/quic_ssl-t.h | 2 ++ src/cfgparse-ssl.c | 15 +++++++++++++++ src/quic_ssl.c | 26 +++++++++++--------------- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/include/haproxy/quic_ssl-t.h b/include/haproxy/quic_ssl-t.h index 3c057c6f0..addcf51c7 100644 --- a/include/haproxy/quic_ssl-t.h +++ b/include/haproxy/quic_ssl-t.h @@ -17,5 +17,7 @@ #include extern struct pool_head *pool_head_quic_ssl_sock_ctx; +extern const char *default_quic_ciphersuites; +extern const char *default_quic_curves; #endif /* _HAPROXY_QUIC_SSL_T_H */ diff --git a/src/cfgparse-ssl.c b/src/cfgparse-ssl.c index 3b694def0..e5e539c08 100644 --- a/src/cfgparse-ssl.c +++ b/src/cfgparse-ssl.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -1751,6 +1752,13 @@ static int ssl_sock_init_srv(struct server *s) if (!s->ssl_ctx.ciphersuites) return 1; } +#endif +#ifdef USE_QUIC + if (srv_is_quic(s) && !s->ssl_ctx.ciphersuites) { + s->ssl_ctx.ciphersuites = strdup(default_quic_ciphersuites); + if (!s->ssl_ctx.ciphersuites) + return 1; + } #endif s->ssl_ctx.options |= global_ssl.connect_default_ssloptions; s->ssl_ctx.methods.flags |= global_ssl.connect_default_sslmethods.flags; @@ -1784,6 +1792,13 @@ static int ssl_sock_init_srv(struct server *s) return 1; } #endif +#ifdef USE_QUIC + if (srv_is_quic(s) && !s->ssl_ctx.curves) { + s->ssl_ctx.curves = strdup(default_quic_curves); + if (!s->ssl_ctx.curves) + return 1; + } +#endif if (global_ssl.renegotiate && !s->ssl_ctx.renegotiate) s->ssl_ctx.renegotiate = global_ssl.renegotiate; diff --git a/src/quic_ssl.c b/src/quic_ssl.c index 32d5982e5..8c334ce17 100644 --- a/src/quic_ssl.c +++ b/src/quic_ssl.c @@ -12,12 +12,12 @@ #include DECLARE_TYPED_POOL(pool_head_quic_ssl_sock_ctx, "quic_ssl_sock_ctx", struct ssl_sock_ctx); -const char *quic_ciphers = "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384" +const char *default_quic_ciphersuites = "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384" ":TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_CCM_SHA256"; #ifdef HAVE_OPENSSL_QUIC -const char *quic_groups = "X25519:P-256:P-384:P-521:X25519MLKEM768"; +const char *default_quic_curves = "X25519:P-256:P-384:P-521:X25519MLKEM768"; #else -const char *quic_groups = "X25519:P-256:P-384:P-521"; +const char *default_quic_curves = "X25519:P-256:P-384:P-521"; #endif @@ -742,8 +742,11 @@ static SSL_QUIC_METHOD ha_quic_method = { */ int ssl_quic_initial_ctx(struct bind_conf *bind_conf) { - struct ssl_bind_conf __maybe_unused *ssl_conf_cur; int cfgerr = 0; + const char *ciphersuites = bind_conf->ssl_conf.ciphersuites ? + bind_conf->ssl_conf.ciphersuites : default_quic_ciphersuites; + const char *curves = bind_conf->ssl_conf.curves ? + bind_conf->ssl_conf.curves : default_quic_curves; long options = (SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) | @@ -760,7 +763,8 @@ int ssl_quic_initial_ctx(struct bind_conf *bind_conf) SSL_CTX_set_mode(ctx, SSL_MODE_RELEASE_BUFFERS); SSL_CTX_set_min_proto_version(ctx, TLS1_3_VERSION); SSL_CTX_set_max_proto_version(ctx, TLS1_3_VERSION); - if (SSL_CTX_set_ciphersuites(ctx, quic_ciphers) != 1) { + + if (SSL_CTX_set_ciphersuites(ctx, ciphersuites) != 1) { ha_warning("Binding [%s:%d] for %s %s: default QUIC cipher" " suites setting failed.\n", bind_conf->file, bind_conf->line, @@ -769,17 +773,14 @@ int ssl_quic_initial_ctx(struct bind_conf *bind_conf) cfgerr++; } -#ifndef HAVE_OPENSSL_QUICTLS - /* TODO: this should also work with QUICTLS */ - if (SSL_CTX_set1_groups_list(ctx, quic_groups) != 1) { + if (SSL_CTX_set1_curves_list(ctx, curves) != 1) { ha_warning("Binding [%s:%d] for %s %s: default QUIC cipher" - " groups setting failed.\n", + " curves setting failed.\n", bind_conf->file, bind_conf->line, proxy_type_str(bind_conf->frontend), bind_conf->frontend->id); cfgerr++; } -#endif if (bind_conf->ssl_conf.early_data) { #if !defined(HAVE_SSL_0RTT_QUIC) @@ -826,11 +827,6 @@ SSL_CTX *ssl_quic_srv_new_ssl_ctx(void) SSL_CTX_set_min_proto_version(ctx, TLS1_3_VERSION); SSL_CTX_set_max_proto_version(ctx, TLS1_3_VERSION); - if (SSL_CTX_set_ciphersuites(ctx, quic_ciphers) != 1) - goto err; - - if (SSL_CTX_set1_groups_list(ctx, quic_groups) != 1) - goto err; #ifdef USE_QUIC_OPENSSL_COMPAT if (!quic_tls_compat_init(NULL, ctx))