]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
REORG: ssl: move the SNI selection code in ssl_clienthello.c
authorWilliam Lallemand <wlallemand@haproxy.com>
Thu, 13 Jun 2024 14:45:48 +0000 (16:45 +0200)
committerWilliam Lallemand <wlallemand@haproxy.com>
Thu, 13 Jun 2024 14:48:17 +0000 (16:48 +0200)
Move the code which is used to select the final certificate with the
clienthello callback. ssl_sock_client_sni_pool need to be exposed from
outside ssl_sock.c

Makefile
include/haproxy/ssl_sock-t.h
src/ssl_clienthello.c [new file with mode: 0644]
src/ssl_sock.c

index cefee0f5df25c7f3307454f67f3cdbe33ca572d1..ab3f3b1d375c73b6b4a1b56740485e83d16d018e 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -626,7 +626,7 @@ ifneq ($(USE_OPENSSL:0=),)
     SSL_LDFLAGS   := $(if $(SSL_LIB),-L$(SSL_LIB)) -lssl -lcrypto
   endif
   USE_SSL         := $(if $(USE_SSL:0=),$(USE_SSL:0=),implicit)
-  OPTIONS_OBJS += src/ssl_sock.o src/ssl_ckch.o src/ssl_ocsp.o src/ssl_crtlist.o src/ssl_sample.o src/cfgparse-ssl.o src/ssl_gencert.o src/ssl_utils.o src/jwt.o
+  OPTIONS_OBJS += src/ssl_sock.o src/ssl_ckch.o src/ssl_ocsp.o src/ssl_crtlist.o src/ssl_sample.o src/cfgparse-ssl.o src/ssl_gencert.o src/ssl_utils.o src/jwt.o src/ssl_clienthello.o
 endif
 
 ifneq ($(USE_ENGINE:0=),)
index d111883ddda20b1ce57055814f3bd8665f03cd78..a8c37e6bf20e3c5f6922bccedae62285a8e51d83 100644 (file)
@@ -321,5 +321,7 @@ extern const char *SSL_SOCK_KEYTYPE_NAMES[];
 
 #define SSL_SOCK_NUM_KEYTYPES 3
 
+extern struct pool_head *ssl_sock_client_sni_pool;
+
 #endif /* USE_OPENSSL */
 #endif /* _HAPROXY_SSL_SOCK_T_H */
diff --git a/src/ssl_clienthello.c b/src/ssl_clienthello.c
new file mode 100644 (file)
index 0000000..4afa5df
--- /dev/null
@@ -0,0 +1,681 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/* Note: do NOT include openssl/xxx.h here, do it in openssl-compat.h */
+#define _GNU_SOURCE
+#include <ctype.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <import/ebpttree.h>
+#include <import/ebsttree.h>
+
+#include <haproxy/openssl-compat.h>
+#include <haproxy/proto_tcp.h>
+#include <haproxy/quic_conn.h>
+#include <haproxy/quic_openssl_compat.h>
+#include <haproxy/quic_tp.h>
+#include <haproxy/ssl_ckch.h>
+#include <haproxy/ssl_gencert.h>
+#include <haproxy/ssl_sock.h>
+
+static void ssl_sock_switchctx_set(SSL *ssl, SSL_CTX *ctx)
+{
+       SSL_set_verify(ssl, SSL_CTX_get_verify_mode(ctx), ssl_sock_bind_verifycbk);
+       SSL_set_client_CA_list(ssl, SSL_dup_CA_list(SSL_CTX_get_client_CA_list(ctx)));
+       SSL_set_SSL_CTX(ssl, ctx);
+}
+
+/*
+ * Return the right sni_ctx for a <bind_conf> and a chosen <servername> (must be in lowercase)
+ * RSA <have_rsa_sig> and ECDSA <have_ecdsa_sig> capabilities of the client can also be used.
+ *
+ * This function does a lookup in the bind_conf sni tree so the caller should lock its tree.
+ */
+struct sni_ctx *ssl_sock_chose_sni_ctx(struct bind_conf *s, const char *servername,
+                                                             int have_rsa_sig, int have_ecdsa_sig)
+{
+       struct ebmb_node *node, *n, *node_ecdsa = NULL, *node_rsa = NULL, *node_anonymous = NULL;
+       const char *wildp = NULL;
+       int i;
+
+       /* look for the first dot for wildcard search */
+       for (i = 0; servername[i] != '\0'; i++) {
+               if (servername[i] == '.') {
+                       wildp = &servername[i];
+                       break;
+               }
+       }
+       /* if the servername is empty look for the default in the wildcard list */
+       if (!*servername)
+               wildp = servername;
+
+       /* Look for an ECDSA, RSA and DSA certificate, first in the single
+        * name and if not found in the wildcard  */
+       for (i = 0; i < 2; i++) {
+               if (i == 0)     /* lookup in full qualified names */
+                       node = ebst_lookup(&s->sni_ctx, trash.area);
+               else if (i == 1 && wildp)  /* lookup in wildcards names */
+                       node = ebst_lookup(&s->sni_w_ctx, wildp);
+               else
+                       break;
+
+               for (n = node; n; n = ebmb_next_dup(n)) {
+
+                       /* lookup a not neg filter */
+                       if (!container_of(n, struct sni_ctx, name)->neg) {
+                               struct sni_ctx *sni, *sni_tmp;
+                               int skip = 0;
+
+                               if (i == 1 && wildp) { /* wildcard */
+                                       /* If this is a wildcard, look for an exclusion on the same crt-list line */
+                                       sni = container_of(n, struct sni_ctx, name);
+                                       list_for_each_entry(sni_tmp, &sni->ckch_inst->sni_ctx, by_ckch_inst) {
+                                               if (sni_tmp->neg && (strcmp((const char *)sni_tmp->name.key, trash.area) == 0)) {
+                                                       skip = 1;
+                                                       break;
+                                               }
+                                       }
+                                       if (skip)
+                                               continue;
+                               }
+
+                               switch(container_of(n, struct sni_ctx, name)->kinfo.sig) {
+                               case TLSEXT_signature_ecdsa:
+                                       if (!node_ecdsa)
+                                               node_ecdsa = n;
+                                       break;
+                               case TLSEXT_signature_rsa:
+                                       if (!node_rsa)
+                                               node_rsa = n;
+                                       break;
+                               default: /* TLSEXT_signature_anonymous|dsa */
+                                       if (!node_anonymous)
+                                               node_anonymous = n;
+                                       break;
+                               }
+                       }
+               }
+       }
+       /* Once the certificates are found, select them depending on what is
+        * supported in the client and by key_signature priority order: EDSA >
+        * RSA > DSA */
+       if (have_ecdsa_sig && node_ecdsa)
+               node = node_ecdsa;
+       else if (have_rsa_sig && node_rsa)
+               node = node_rsa;
+       else if (node_anonymous)
+               node = node_anonymous;
+       else if (node_ecdsa)
+               node = node_ecdsa;      /* no ecdsa signature case (< TLSv1.2) */
+       else
+               node = node_rsa;        /* no rsa signature case (far far away) */
+
+       if (node)
+               return container_of(node, struct sni_ctx, name);
+
+       return NULL;
+}
+
+#ifdef HAVE_SSL_CLIENT_HELLO_CB
+
+int ssl_sock_switchctx_err_cbk(SSL *ssl, int *al, void *priv)
+{
+       struct bind_conf *s = priv;
+       (void)al; /* shut gcc stupid warning */
+
+       if (SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name) || (s->options & BC_O_GENERATE_CERTS))
+               return SSL_TLSEXT_ERR_OK;
+       return SSL_TLSEXT_ERR_NOACK;
+}
+
+#ifdef OPENSSL_IS_BORINGSSL
+int ssl_sock_switchctx_cbk(const struct ssl_early_callback_ctx *ctx)
+{
+       SSL *ssl = ctx->ssl;
+#else
+int ssl_sock_switchctx_cbk(SSL *ssl, int *al, void *arg)
+{
+#endif
+       struct connection *conn = SSL_get_ex_data(ssl, ssl_app_data_index);
+#ifdef USE_QUIC
+       struct quic_conn *qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
+#endif /* USE_QUIC */
+       struct bind_conf *s = NULL;
+       const uint8_t *extension_data;
+       size_t extension_len;
+       int has_rsa_sig = 0, has_ecdsa_sig = 0;
+       struct sni_ctx *sni_ctx;
+       const char *servername;
+       size_t servername_len = 0;
+       int default_lookup = 0; /* did we lookup for a default yet? */
+       int allow_early = 0;
+       int i;
+
+       if (conn)
+               s = __objt_listener(conn->target)->bind_conf;
+#ifdef USE_QUIC
+       else if (qc)
+               s = qc->li->bind_conf;
+#endif /* USE_QUIC */
+
+       if (!s) {
+               /* must never happen */
+               ABORT_NOW();
+               return 0;
+       }
+
+#ifdef USE_QUIC
+       if (qc) {
+               /* Look for the QUIC transport parameters. */
+#ifdef OPENSSL_IS_BORINGSSL
+               if (!SSL_early_callback_ctx_extension_get(ctx, qc->tps_tls_ext,
+                                                         &extension_data, &extension_len))
+#else
+               if (!SSL_client_hello_get0_ext(ssl, qc->tps_tls_ext,
+                                              &extension_data, &extension_len))
+#endif
+               {
+                       /* This is not redundant. It we only return 0 without setting
+                        * <*al>, this has as side effect to generate another TLS alert
+                        * which would be set after calling quic_set_tls_alert().
+                        */
+                       *al = SSL_AD_MISSING_EXTENSION;
+                       quic_set_tls_alert(qc, SSL_AD_MISSING_EXTENSION);
+                       return 0;
+               }
+
+               if (!quic_transport_params_store(qc, 0, extension_data,
+                                                extension_data + extension_len))
+                       goto abort;
+
+               qc->flags |= QUIC_FL_CONN_TX_TP_RECEIVED;
+       }
+#endif /* USE_QUIC */
+
+       if (s->ssl_conf.early_data)
+               allow_early = 1;
+#ifdef OPENSSL_IS_BORINGSSL
+       if (SSL_early_callback_ctx_extension_get(ctx, TLSEXT_TYPE_server_name,
+                                                &extension_data, &extension_len)) {
+#else
+       if (SSL_client_hello_get0_ext(ssl, TLSEXT_TYPE_server_name, &extension_data, &extension_len)) {
+#endif
+               /*
+                * The server_name extension was given too much extensibility when it
+                * was written, so parsing the normal case is a bit complex.
+                */
+               size_t len;
+               if (extension_len <= 2)
+                       goto abort;
+               /* Extract the length of the supplied list of names. */
+               len = (*extension_data++) << 8;
+               len |= *extension_data++;
+               if (len + 2 != extension_len)
+                       goto abort;
+               /*
+                * The list in practice only has a single element, so we only consider
+                * the first one.
+                */
+               if (len == 0 || *extension_data++ != TLSEXT_NAMETYPE_host_name)
+                       goto abort;
+               extension_len = len - 1;
+               /* Now we can finally pull out the byte array with the actual hostname. */
+               if (extension_len <= 2)
+                       goto abort;
+               len = (*extension_data++) << 8;
+               len |= *extension_data++;
+               if (len == 0 || len + 2 > extension_len || len > TLSEXT_MAXLEN_host_name
+                   || memchr(extension_data, 0, len) != NULL)
+                       goto abort;
+               servername = (char *)extension_data;
+               servername_len = len;
+       } else {
+#if (!defined SSL_NO_GENERATE_CERTIFICATES)
+               if (s->options & BC_O_GENERATE_CERTS && ssl_sock_generate_certificate_from_conn(s, ssl)) {
+                       goto allow_early;
+               }
+#endif
+
+               /* no servername field is not compatible with strict-sni */
+               if (s->strict_sni)
+                       goto abort;
+
+               /* without servername extension, look for the defaults which is
+                * defined by an empty servername string */
+               servername = "";
+               servername_len = 0;
+               default_lookup = 1;
+       }
+
+       /* extract/check clientHello information */
+#ifdef OPENSSL_IS_BORINGSSL
+       if (SSL_early_callback_ctx_extension_get(ctx, TLSEXT_TYPE_signature_algorithms, &extension_data, &extension_len)) {
+#else
+       if (SSL_client_hello_get0_ext(ssl, TLSEXT_TYPE_signature_algorithms, &extension_data, &extension_len)) {
+#endif
+               uint8_t sign;
+               size_t len;
+               if (extension_len < 2)
+                       goto abort;
+               len = (*extension_data++) << 8;
+               len |= *extension_data++;
+               if (len + 2 != extension_len)
+                       goto abort;
+               if (len % 2 != 0)
+                       goto abort;
+               for (; len > 0; len -= 2) {
+                       extension_data++; /* hash */
+                       sign = *extension_data++;
+                       switch (sign) {
+                       case TLSEXT_signature_rsa:
+                               has_rsa_sig = 1;
+                               break;
+                       case TLSEXT_signature_ecdsa:
+                               has_ecdsa_sig = 1;
+                               break;
+                       default:
+                               continue;
+                       }
+                       if (has_ecdsa_sig && has_rsa_sig)
+                               break;
+               }
+       } else {
+               /* without TLSEXT_TYPE_signature_algorithms extension (< TLSv1.2) */
+               has_rsa_sig = 1;
+       }
+       if (has_ecdsa_sig) {  /* in very rare case: has ecdsa sign but not a ECDSA cipher */
+               const SSL_CIPHER *cipher;
+               STACK_OF(SSL_CIPHER) *ha_ciphers; /* haproxy side ciphers */
+               uint32_t cipher_id;
+               size_t len;
+               const uint8_t *cipher_suites;
+
+               ha_ciphers = SSL_get_ciphers(ssl);
+               has_ecdsa_sig = 0;
+
+#ifdef OPENSSL_IS_BORINGSSL
+               len = ctx->cipher_suites_len;
+               cipher_suites = ctx->cipher_suites;
+#else
+               len = SSL_client_hello_get0_ciphers(ssl, &cipher_suites);
+#endif
+               if (len % 2 != 0)
+                       goto abort;
+               for (; len != 0; len -= 2, cipher_suites += 2) {
+#ifdef OPENSSL_IS_BORINGSSL
+                       uint16_t cipher_suite = (cipher_suites[0] << 8) | cipher_suites[1];
+                       cipher = SSL_get_cipher_by_value(cipher_suite);
+#else
+                       cipher = SSL_CIPHER_find(ssl, cipher_suites);
+#endif
+                       if (!cipher)
+                               continue;
+
+                       /* check if this cipher is available in haproxy configuration */
+                       if (sk_SSL_CIPHER_find(ha_ciphers, cipher) == -1)
+                               continue;
+
+                       cipher_id = SSL_CIPHER_get_id(cipher);
+                       /* skip the SCSV "fake" signaling ciphersuites because they are NID_auth_any (RFC 7507) */
+                       if (cipher_id == SSL3_CK_SCSV || cipher_id == SSL3_CK_FALLBACK_SCSV)
+                               continue;
+
+                       if (SSL_CIPHER_get_auth_nid(cipher) == NID_auth_ecdsa
+                           || SSL_CIPHER_get_auth_nid(cipher) == NID_auth_any) {
+                               has_ecdsa_sig = 1;
+                               break;
+                       }
+               }
+       }
+
+sni_lookup:
+       /* we need to transform this a NULL-ended string in lowecase */
+       for (i = 0; i < trash.size && i < servername_len; i++)
+               trash.area[i] = tolower(servername[i]);
+       trash.area[i] = 0;
+
+       HA_RWLOCK_RDLOCK(SNI_LOCK, &s->sni_lock);
+       sni_ctx = ssl_sock_chose_sni_ctx(s, trash.area, has_rsa_sig, has_ecdsa_sig);
+       if (sni_ctx) {
+               /* switch ctx */
+               struct ssl_bind_conf *conf = sni_ctx->conf;
+               ssl_sock_switchctx_set(ssl, sni_ctx->ctx);
+               if (conf) {
+                       methodVersions[conf->ssl_methods.min].ssl_set_version(ssl, SET_MIN);
+                       methodVersions[conf->ssl_methods.max].ssl_set_version(ssl, SET_MAX);
+                       if (conf->early_data)
+                               allow_early = 1;
+               }
+               HA_RWLOCK_RDUNLOCK(SNI_LOCK, &s->sni_lock);
+               goto allow_early;
+       }
+
+       HA_RWLOCK_RDUNLOCK(SNI_LOCK, &s->sni_lock);
+#if (!defined SSL_NO_GENERATE_CERTIFICATES)
+       if (s->options & BC_O_GENERATE_CERTS && ssl_sock_generate_certificate(trash.area, s, ssl)) {
+               /* switch ctx done in ssl_sock_generate_certificate */
+               goto allow_early;
+       }
+#endif
+
+       if (!s->strict_sni && !default_lookup) {
+               /* we didn't find a SNI, and we didn't look for a default
+                * look again to find a matching default cert */
+               servername = "";
+               servername_len = 0;
+               default_lookup = 1;
+
+               goto sni_lookup;
+       }
+
+       /* We are about to raise an handshake error so the servername extension
+        * callback will never be called and the SNI will never be stored in the
+        * SSL context. In order for the ssl_fc_sni sample fetch to still work
+        * in such a case, we store the SNI ourselves as an ex_data information
+        * in the SSL context.
+        */
+       {
+               char *client_sni = pool_alloc(ssl_sock_client_sni_pool);
+               if (client_sni) {
+                       strncpy(client_sni, servername, TLSEXT_MAXLEN_host_name);
+                       client_sni[TLSEXT_MAXLEN_host_name] = '\0';
+                       SSL_set_ex_data(ssl, ssl_client_sni_index, client_sni);
+               }
+       }
+
+       /* other cases fallback on abort, if strict-sni is set but no node was found */
+
+ abort:
+       /* abort handshake (was SSL_TLSEXT_ERR_ALERT_FATAL) */
+       if (conn)
+               conn->err_code = CO_ER_SSL_HANDSHAKE;
+#ifdef OPENSSL_IS_BORINGSSL
+       return ssl_select_cert_error;
+#else
+       *al = SSL_AD_UNRECOGNIZED_NAME;
+       return 0;
+#endif
+
+allow_early:
+#ifdef OPENSSL_IS_BORINGSSL
+       if (allow_early)
+               SSL_set_early_data_enabled(ssl, 1);
+#else
+       if (!allow_early)
+               SSL_set_max_early_data(ssl, 0);
+#endif
+       return 1;
+}
+
+#else /* ! HAVE_SSL_CLIENT_HELLO_CB  */
+
+/* Sets the SSL ctx of <ssl> to match the advertised server name. Returns a
+ * warning when no match is found, which implies the default (first) cert
+ * will keep being used.
+ */
+int ssl_sock_switchctx_cbk(SSL *ssl, int *al, void *priv)
+{
+       const char *servername;
+       const char *wildp = NULL;
+       struct ebmb_node *node, *n;
+       struct bind_conf *s = priv;
+       int default_lookup = 0; /* did we lookup for a default yet? */
+#ifdef USE_QUIC
+       const uint8_t *extension_data;
+       size_t extension_len;
+       struct quic_conn *qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
+#endif /* USE_QUIC */
+       int i;
+       (void)al; /* shut gcc stupid warning */
+
+#ifdef USE_QUIC
+       if (qc) {
+
+               /* Look for the QUIC transport parameters. */
+               SSL_get_peer_quic_transport_params(ssl, &extension_data, &extension_len);
+               if (extension_len == 0) {
+                       /* This is not redundant. It we only return 0 without setting
+                        * <*al>, this has as side effect to generate another TLS alert
+                        * which would be set after calling quic_set_tls_alert().
+                        */
+                       *al = SSL_AD_MISSING_EXTENSION;
+                       quic_set_tls_alert(qc, SSL_AD_MISSING_EXTENSION);
+                       return SSL_TLSEXT_ERR_NOACK;
+               }
+
+               if (!quic_transport_params_store(qc, 0, extension_data,
+                                                extension_data + extension_len))
+                       return SSL_TLSEXT_ERR_NOACK;
+
+               qc->flags |= QUIC_FL_CONN_TX_TP_RECEIVED;
+       }
+#endif /* USE_QUIC */
+
+       servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
+       if (!servername) {
+#if (!defined SSL_NO_GENERATE_CERTIFICATES)
+               if (s->options & BC_O_GENERATE_CERTS && ssl_sock_generate_certificate_from_conn(s, ssl))
+                       return SSL_TLSEXT_ERR_OK;
+#endif
+               if (s->strict_sni)
+                       return SSL_TLSEXT_ERR_ALERT_FATAL;
+
+               /* without servername extension, look for the defaults which is
+                * defined by an empty servername string */
+               servername = "";
+               default_lookup = 1;
+       }
+
+sni_lookup:
+
+       for (i = 0; i < trash.size; i++) {
+               if (!servername[i])
+                       break;
+               trash.area[i] = tolower((unsigned char)servername[i]);
+               if (!wildp && (trash.area[i] == '.'))
+                       wildp = &trash.area[i];
+       }
+       trash.area[i] = 0;
+       if(!*trash.area) /* handle the default which in wildcard tree */
+               wildp = trash.area;
+
+       HA_RWLOCK_RDLOCK(SNI_LOCK, &s->sni_lock);
+       node = NULL;
+       /* lookup in full qualified names */
+       for (n = ebst_lookup(&s->sni_ctx, trash.area); n; n = ebmb_next_dup(n)) {
+               /* lookup a not neg filter */
+               if (!container_of(n, struct sni_ctx, name)->neg) {
+                       node = n;
+                       break;
+               }
+       }
+       if (!node && wildp) {
+               /* lookup in wildcards names */
+               for (n = ebst_lookup(&s->sni_w_ctx, wildp); n; n = ebmb_next_dup(n)) {
+                       /* lookup a not neg filter */
+                       if (!container_of(n, struct sni_ctx, name)->neg) {
+                               node = n;
+                               break;
+                       }
+               }
+       }
+       if (!node) {
+#if (!defined SSL_NO_GENERATE_CERTIFICATES)
+               if (s->options & BC_O_GENERATE_CERTS && ssl_sock_generate_certificate(servername, s, ssl)) {
+                       /* switch ctx done in ssl_sock_generate_certificate */
+                       HA_RWLOCK_RDUNLOCK(SNI_LOCK, &s->sni_lock);
+                       return SSL_TLSEXT_ERR_OK;
+               }
+#endif
+               HA_RWLOCK_RDUNLOCK(SNI_LOCK, &s->sni_lock);
+
+               if (!s->strict_sni && !default_lookup) {
+                       /* we didn't find a SNI, and we didn't look for a default
+                        * look again to find a matching default cert */
+                       servername = "";
+                       default_lookup = 1;
+
+                       goto sni_lookup;
+               }
+               return SSL_TLSEXT_ERR_ALERT_FATAL;
+       }
+
+#if defined(OPENSSL_IS_AWSLC)
+       /* Note that ssl_sock_switchctx_set() calls SSL_set_SSL_CTX() which propagates the
+        * "early data enabled" setting from the SSL_CTX object to the SSL objects.
+        * So enable early data for this SSL_CTX context if configured.
+        */
+       if (s->ssl_conf.early_data)
+               SSL_CTX_set_early_data_enabled(container_of(node, struct sni_ctx, name)->ctx, 1);
+#endif
+       /* switch ctx */
+       ssl_sock_switchctx_set(ssl, container_of(node, struct sni_ctx, name)->ctx);
+       HA_RWLOCK_RDUNLOCK(SNI_LOCK, &s->sni_lock);
+       return SSL_TLSEXT_ERR_OK;
+}
+#endif /* (!) OPENSSL_IS_BORINGSSL */
+
+#if defined(USE_OPENSSL_WOLFSSL)
+/* This implement the equivalent of the clientHello Callback but using the cert_cb.
+ * WolfSSL is able to extract the sigalgs and ciphers of the client byt using the API
+ * provided in https://github.com/wolfSSL/wolfssl/pull/6963
+ *
+ * Not activated for now since the PR is not merged.
+ */
+static int ssl_sock_switchctx_wolfSSL_cbk(WOLFSSL* ssl, void* arg)
+{
+       struct bind_conf *s = arg;
+       int has_rsa_sig = 0, has_ecdsa_sig = 0;
+       const char *servername;
+       int default_lookup = 0;
+       struct sni_ctx *sni_ctx;
+       int i;
+
+       if (!s) {
+               /* must never happen */
+               ABORT_NOW();
+               return 0;
+       }
+
+       servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
+       if (!servername) {
+               if (s->strict_sni)
+                       goto abort;
+
+               /* without servername extension, look for the defaults which is
+                * defined by an empty servername string */
+               servername = "";
+               default_lookup = 1;
+       }
+
+       /* extract sigalgs and ciphers */
+       {
+               const byte* suites = NULL;
+               word16 suiteSz = 0;
+               const byte* hashSigAlgo = NULL;
+               word16 hashSigAlgoSz = 0;
+               word16 idx = 0;
+
+               wolfSSL_get_client_suites_sigalgs(ssl, &suites, &suiteSz, &hashSigAlgo, &hashSigAlgoSz);
+               if (suites == NULL || suiteSz == 0 || hashSigAlgo == NULL || hashSigAlgoSz == 0)
+                       return 0;
+
+               if (SSL_version(ssl) != TLS1_3_VERSION) {
+
+                       /* with TLS <= 1.2, we must use the auth which is provided by the cipher, but we don't need to
+                        * consider the auth provided by the signature algorithms */
+
+                       for (idx = 0; idx < suiteSz; idx += 2) {
+                               WOLFSSL_CIPHERSUITE_INFO info;
+                               info = wolfSSL_get_ciphersuite_info(suites[idx], suites[idx+1]);
+                               if (info.rsaAuth)
+                                       has_rsa_sig = 1;
+                               else if (info.eccAuth)
+                                       has_ecdsa_sig = 1;
+                       }
+               } else {
+                       /* with TLS >= 1.3, we must use the auth which is provided by the signature algorithms because
+                        * the ciphers does not provide the auth */
+
+                       for (idx = 0; idx < hashSigAlgoSz; idx += 2) {
+                               int hashAlgo;
+                               int sigAlgo;
+
+                               wolfSSL_get_sigalg_info(hashSigAlgo[idx+0], hashSigAlgo[idx+1], &hashAlgo, &sigAlgo);
+
+                               if (sigAlgo == RSAk || sigAlgo == RSAPSSk)
+                                       has_rsa_sig = 1;
+                               else if (sigAlgo == ECDSAk)
+                                       has_ecdsa_sig = 1;
+
+                       }
+               }
+       }
+
+sni_lookup:
+
+       /* we need to transform this into a NULL-ended string in lowecase */
+       for (i = 0; i < trash.size && servername[i] != '\0'; i++)
+               trash.area[i] = tolower(servername[i]);
+       trash.area[i] = 0;
+       servername = trash.area;
+
+       HA_RWLOCK_RDLOCK(SNI_LOCK, &s->sni_lock);
+       sni_ctx = ssl_sock_chose_sni_ctx(s, servername, has_rsa_sig, has_ecdsa_sig);
+       if (sni_ctx) {
+               /* switch ctx */
+               struct ssl_bind_conf *conf = sni_ctx->conf;
+               ssl_sock_switchctx_set(ssl, sni_ctx->ctx);
+               if (conf) {
+                       methodVersions[conf->ssl_methods.min].ssl_set_version(ssl, SET_MIN);
+                       methodVersions[conf->ssl_methods.max].ssl_set_version(ssl, SET_MAX);
+               }
+               HA_RWLOCK_RDUNLOCK(SNI_LOCK, &s->sni_lock);
+               goto allow_early;
+       }
+
+       HA_RWLOCK_RDUNLOCK(SNI_LOCK, &s->sni_lock);
+       if (!s->strict_sni && !default_lookup) {
+               /* we didn't find a SNI, and we didn't look for a default
+                * look again to find a matching default cert */
+               servername = "";
+               default_lookup = 1;
+
+               goto sni_lookup;
+       }
+
+       /* We are about to raise an handshake error so the servername extension
+        * callback will never be called and the SNI will never be stored in the
+        * SSL context. In order for the ssl_fc_sni sample fetch to still work
+        * in such a case, we store the SNI ourselves as an ex_data information
+        * in the SSL context.
+        */
+       {
+               char *client_sni = pool_alloc(ssl_sock_client_sni_pool);
+               if (client_sni) {
+                       strncpy(client_sni, servername, TLSEXT_MAXLEN_host_name);
+                       client_sni[TLSEXT_MAXLEN_host_name] = '\0';
+                       SSL_set_ex_data(ssl, ssl_client_sni_index, client_sni);
+               }
+       }
+
+       /* other cases fallback on abort, if strict-sni is set but no node was found */
+
+ abort:
+       /* abort handshake (was SSL_TLSEXT_ERR_ALERT_FATAL) */
+       return 0;
+
+allow_early:
+       return 1;
+}
+#endif
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ * End:
+ */
index 8bd609994203c01d564c1042c5d57335114ff67f..87ee42f1a700bfb1c301ba2dd41cceccb6b1be14 100644 (file)
@@ -149,7 +149,7 @@ static BIO_METHOD *ha_meth;
 
 DECLARE_STATIC_POOL(ssl_sock_ctx_pool, "ssl_sock_ctx", sizeof(struct ssl_sock_ctx));
 
-DECLARE_STATIC_POOL(ssl_sock_client_sni_pool, "ssl_sock_client_sni", TLSEXT_MAXLEN_host_name + 1);
+DECLARE_POOL(ssl_sock_client_sni_pool, "ssl_sock_client_sni", TLSEXT_MAXLEN_host_name + 1);
 
 /* ssl stats module */
 enum {
@@ -2001,658 +2001,6 @@ struct methodVersions methodVersions[] = {
        {SSL_OP_NO_TLSv1_3, MC_SSL_O_NO_TLSV13, ctx_set_TLSv13_func, ssl_set_TLSv13_func, "TLSv1.3"}, /* CONF_TLSV13 */
 };
 
-static void ssl_sock_switchctx_set(SSL *ssl, SSL_CTX *ctx)
-{
-       SSL_set_verify(ssl, SSL_CTX_get_verify_mode(ctx), ssl_sock_bind_verifycbk);
-       SSL_set_client_CA_list(ssl, SSL_dup_CA_list(SSL_CTX_get_client_CA_list(ctx)));
-       SSL_set_SSL_CTX(ssl, ctx);
-}
-
-/*
- * Return the right sni_ctx for a <bind_conf> and a chosen <servername> (must be in lowercase)
- * RSA <have_rsa_sig> and ECDSA <have_ecdsa_sig> capabilities of the client can also be used.
- *
- * This function does a lookup in the bind_conf sni tree so the caller should lock its tree.
- */
-struct sni_ctx *ssl_sock_chose_sni_ctx(struct bind_conf *s, const char *servername,
-                                                             int have_rsa_sig, int have_ecdsa_sig)
-{
-       struct ebmb_node *node, *n, *node_ecdsa = NULL, *node_rsa = NULL, *node_anonymous = NULL;
-       const char *wildp = NULL;
-       int i;
-
-       /* look for the first dot for wildcard search */
-       for (i = 0; servername[i] != '\0'; i++) {
-               if (servername[i] == '.') {
-                       wildp = &servername[i];
-                       break;
-               }
-       }
-       /* if the servername is empty look for the default in the wildcard list */
-       if (!*servername)
-               wildp = servername;
-
-       /* Look for an ECDSA, RSA and DSA certificate, first in the single
-        * name and if not found in the wildcard  */
-       for (i = 0; i < 2; i++) {
-               if (i == 0)     /* lookup in full qualified names */
-                       node = ebst_lookup(&s->sni_ctx, trash.area);
-               else if (i == 1 && wildp)  /* lookup in wildcards names */
-                       node = ebst_lookup(&s->sni_w_ctx, wildp);
-               else
-                       break;
-
-               for (n = node; n; n = ebmb_next_dup(n)) {
-
-                       /* lookup a not neg filter */
-                       if (!container_of(n, struct sni_ctx, name)->neg) {
-                               struct sni_ctx *sni, *sni_tmp;
-                               int skip = 0;
-
-                               if (i == 1 && wildp) { /* wildcard */
-                                       /* If this is a wildcard, look for an exclusion on the same crt-list line */
-                                       sni = container_of(n, struct sni_ctx, name);
-                                       list_for_each_entry(sni_tmp, &sni->ckch_inst->sni_ctx, by_ckch_inst) {
-                                               if (sni_tmp->neg && (strcmp((const char *)sni_tmp->name.key, trash.area) == 0)) {
-                                                       skip = 1;
-                                                       break;
-                                               }
-                                       }
-                                       if (skip)
-                                               continue;
-                               }
-
-                               switch(container_of(n, struct sni_ctx, name)->kinfo.sig) {
-                               case TLSEXT_signature_ecdsa:
-                                       if (!node_ecdsa)
-                                               node_ecdsa = n;
-                                       break;
-                               case TLSEXT_signature_rsa:
-                                       if (!node_rsa)
-                                               node_rsa = n;
-                                       break;
-                               default: /* TLSEXT_signature_anonymous|dsa */
-                                       if (!node_anonymous)
-                                               node_anonymous = n;
-                                       break;
-                               }
-                       }
-               }
-       }
-       /* Once the certificates are found, select them depending on what is
-        * supported in the client and by key_signature priority order: EDSA >
-        * RSA > DSA */
-       if (have_ecdsa_sig && node_ecdsa)
-               node = node_ecdsa;
-       else if (have_rsa_sig && node_rsa)
-               node = node_rsa;
-       else if (node_anonymous)
-               node = node_anonymous;
-       else if (node_ecdsa)
-               node = node_ecdsa;      /* no ecdsa signature case (< TLSv1.2) */
-       else
-               node = node_rsa;        /* no rsa signature case (far far away) */
-
-       if (node)
-               return container_of(node, struct sni_ctx, name);
-
-       return NULL;
-}
-
-#ifdef HAVE_SSL_CLIENT_HELLO_CB
-
-int ssl_sock_switchctx_err_cbk(SSL *ssl, int *al, void *priv)
-{
-       struct bind_conf *s = priv;
-       (void)al; /* shut gcc stupid warning */
-
-       if (SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name) || (s->options & BC_O_GENERATE_CERTS))
-               return SSL_TLSEXT_ERR_OK;
-       return SSL_TLSEXT_ERR_NOACK;
-}
-
-#ifdef OPENSSL_IS_BORINGSSL
-int ssl_sock_switchctx_cbk(const struct ssl_early_callback_ctx *ctx)
-{
-       SSL *ssl = ctx->ssl;
-#else
-int ssl_sock_switchctx_cbk(SSL *ssl, int *al, void *arg)
-{
-#endif
-       struct connection *conn = SSL_get_ex_data(ssl, ssl_app_data_index);
-#ifdef USE_QUIC
-       struct quic_conn *qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
-#endif /* USE_QUIC */
-       struct bind_conf *s = NULL;
-       const uint8_t *extension_data;
-       size_t extension_len;
-       int has_rsa_sig = 0, has_ecdsa_sig = 0;
-       struct sni_ctx *sni_ctx;
-       const char *servername;
-       size_t servername_len = 0;
-       int default_lookup = 0; /* did we lookup for a default yet? */
-       int allow_early = 0;
-       int i;
-
-       if (conn)
-               s = __objt_listener(conn->target)->bind_conf;
-#ifdef USE_QUIC
-       else if (qc)
-               s = qc->li->bind_conf;
-#endif /* USE_QUIC */
-
-       if (!s) {
-               /* must never happen */
-               ABORT_NOW();
-               return 0;
-       }
-
-#ifdef USE_QUIC
-       if (qc) {
-               /* Look for the QUIC transport parameters. */
-#ifdef OPENSSL_IS_BORINGSSL
-               if (!SSL_early_callback_ctx_extension_get(ctx, qc->tps_tls_ext,
-                                                         &extension_data, &extension_len))
-#else
-               if (!SSL_client_hello_get0_ext(ssl, qc->tps_tls_ext,
-                                              &extension_data, &extension_len))
-#endif
-               {
-                       /* This is not redundant. It we only return 0 without setting
-                        * <*al>, this has as side effect to generate another TLS alert
-                        * which would be set after calling quic_set_tls_alert().
-                        */
-                       *al = SSL_AD_MISSING_EXTENSION;
-                       quic_set_tls_alert(qc, SSL_AD_MISSING_EXTENSION);
-                       return 0;
-               }
-
-               if (!quic_transport_params_store(qc, 0, extension_data,
-                                                extension_data + extension_len))
-                       goto abort;
-
-               qc->flags |= QUIC_FL_CONN_TX_TP_RECEIVED;
-       }
-#endif /* USE_QUIC */
-
-       if (s->ssl_conf.early_data)
-               allow_early = 1;
-#ifdef OPENSSL_IS_BORINGSSL
-       if (SSL_early_callback_ctx_extension_get(ctx, TLSEXT_TYPE_server_name,
-                                                &extension_data, &extension_len)) {
-#else
-       if (SSL_client_hello_get0_ext(ssl, TLSEXT_TYPE_server_name, &extension_data, &extension_len)) {
-#endif
-               /*
-                * The server_name extension was given too much extensibility when it
-                * was written, so parsing the normal case is a bit complex.
-                */
-               size_t len;
-               if (extension_len <= 2)
-                       goto abort;
-               /* Extract the length of the supplied list of names. */
-               len = (*extension_data++) << 8;
-               len |= *extension_data++;
-               if (len + 2 != extension_len)
-                       goto abort;
-               /*
-                * The list in practice only has a single element, so we only consider
-                * the first one.
-                */
-               if (len == 0 || *extension_data++ != TLSEXT_NAMETYPE_host_name)
-                       goto abort;
-               extension_len = len - 1;
-               /* Now we can finally pull out the byte array with the actual hostname. */
-               if (extension_len <= 2)
-                       goto abort;
-               len = (*extension_data++) << 8;
-               len |= *extension_data++;
-               if (len == 0 || len + 2 > extension_len || len > TLSEXT_MAXLEN_host_name
-                   || memchr(extension_data, 0, len) != NULL)
-                       goto abort;
-               servername = (char *)extension_data;
-               servername_len = len;
-       } else {
-#if (!defined SSL_NO_GENERATE_CERTIFICATES)
-               if (s->options & BC_O_GENERATE_CERTS && ssl_sock_generate_certificate_from_conn(s, ssl)) {
-                       goto allow_early;
-               }
-#endif
-
-               /* no servername field is not compatible with strict-sni */
-               if (s->strict_sni)
-                       goto abort;
-
-               /* without servername extension, look for the defaults which is
-                * defined by an empty servername string */
-               servername = "";
-               servername_len = 0;
-               default_lookup = 1;
-       }
-
-       /* extract/check clientHello information */
-#ifdef OPENSSL_IS_BORINGSSL
-       if (SSL_early_callback_ctx_extension_get(ctx, TLSEXT_TYPE_signature_algorithms, &extension_data, &extension_len)) {
-#else
-       if (SSL_client_hello_get0_ext(ssl, TLSEXT_TYPE_signature_algorithms, &extension_data, &extension_len)) {
-#endif
-               uint8_t sign;
-               size_t len;
-               if (extension_len < 2)
-                       goto abort;
-               len = (*extension_data++) << 8;
-               len |= *extension_data++;
-               if (len + 2 != extension_len)
-                       goto abort;
-               if (len % 2 != 0)
-                       goto abort;
-               for (; len > 0; len -= 2) {
-                       extension_data++; /* hash */
-                       sign = *extension_data++;
-                       switch (sign) {
-                       case TLSEXT_signature_rsa:
-                               has_rsa_sig = 1;
-                               break;
-                       case TLSEXT_signature_ecdsa:
-                               has_ecdsa_sig = 1;
-                               break;
-                       default:
-                               continue;
-                       }
-                       if (has_ecdsa_sig && has_rsa_sig)
-                               break;
-               }
-       } else {
-               /* without TLSEXT_TYPE_signature_algorithms extension (< TLSv1.2) */
-               has_rsa_sig = 1;
-       }
-       if (has_ecdsa_sig) {  /* in very rare case: has ecdsa sign but not a ECDSA cipher */
-               const SSL_CIPHER *cipher;
-               STACK_OF(SSL_CIPHER) *ha_ciphers; /* haproxy side ciphers */
-               uint32_t cipher_id;
-               size_t len;
-               const uint8_t *cipher_suites;
-
-               ha_ciphers = SSL_get_ciphers(ssl);
-               has_ecdsa_sig = 0;
-
-#ifdef OPENSSL_IS_BORINGSSL
-               len = ctx->cipher_suites_len;
-               cipher_suites = ctx->cipher_suites;
-#else
-               len = SSL_client_hello_get0_ciphers(ssl, &cipher_suites);
-#endif
-               if (len % 2 != 0)
-                       goto abort;
-               for (; len != 0; len -= 2, cipher_suites += 2) {
-#ifdef OPENSSL_IS_BORINGSSL
-                       uint16_t cipher_suite = (cipher_suites[0] << 8) | cipher_suites[1];
-                       cipher = SSL_get_cipher_by_value(cipher_suite);
-#else
-                       cipher = SSL_CIPHER_find(ssl, cipher_suites);
-#endif
-                       if (!cipher)
-                               continue;
-
-                       /* check if this cipher is available in haproxy configuration */
-                       if (sk_SSL_CIPHER_find(ha_ciphers, cipher) == -1)
-                               continue;
-
-                       cipher_id = SSL_CIPHER_get_id(cipher);
-                       /* skip the SCSV "fake" signaling ciphersuites because they are NID_auth_any (RFC 7507) */
-                       if (cipher_id == SSL3_CK_SCSV || cipher_id == SSL3_CK_FALLBACK_SCSV)
-                               continue;
-
-                       if (SSL_CIPHER_get_auth_nid(cipher) == NID_auth_ecdsa
-                           || SSL_CIPHER_get_auth_nid(cipher) == NID_auth_any) {
-                               has_ecdsa_sig = 1;
-                               break;
-                       }
-               }
-       }
-
-sni_lookup:
-       /* we need to transform this a NULL-ended string in lowecase */
-       for (i = 0; i < trash.size && i < servername_len; i++)
-               trash.area[i] = tolower(servername[i]);
-       trash.area[i] = 0;
-
-       HA_RWLOCK_RDLOCK(SNI_LOCK, &s->sni_lock);
-       sni_ctx = ssl_sock_chose_sni_ctx(s, trash.area, has_rsa_sig, has_ecdsa_sig);
-       if (sni_ctx) {
-               /* switch ctx */
-               struct ssl_bind_conf *conf = sni_ctx->conf;
-               ssl_sock_switchctx_set(ssl, sni_ctx->ctx);
-               if (conf) {
-                       methodVersions[conf->ssl_methods.min].ssl_set_version(ssl, SET_MIN);
-                       methodVersions[conf->ssl_methods.max].ssl_set_version(ssl, SET_MAX);
-                       if (conf->early_data)
-                               allow_early = 1;
-               }
-               HA_RWLOCK_RDUNLOCK(SNI_LOCK, &s->sni_lock);
-               goto allow_early;
-       }
-
-       HA_RWLOCK_RDUNLOCK(SNI_LOCK, &s->sni_lock);
-#if (!defined SSL_NO_GENERATE_CERTIFICATES)
-       if (s->options & BC_O_GENERATE_CERTS && ssl_sock_generate_certificate(trash.area, s, ssl)) {
-               /* switch ctx done in ssl_sock_generate_certificate */
-               goto allow_early;
-       }
-#endif
-
-       if (!s->strict_sni && !default_lookup) {
-               /* we didn't find a SNI, and we didn't look for a default
-                * look again to find a matching default cert */
-               servername = "";
-               servername_len = 0;
-               default_lookup = 1;
-
-               goto sni_lookup;
-       }
-
-       /* We are about to raise an handshake error so the servername extension
-        * callback will never be called and the SNI will never be stored in the
-        * SSL context. In order for the ssl_fc_sni sample fetch to still work
-        * in such a case, we store the SNI ourselves as an ex_data information
-        * in the SSL context.
-        */
-       {
-               char *client_sni = pool_alloc(ssl_sock_client_sni_pool);
-               if (client_sni) {
-                       strncpy(client_sni, servername, TLSEXT_MAXLEN_host_name);
-                       client_sni[TLSEXT_MAXLEN_host_name] = '\0';
-                       SSL_set_ex_data(ssl, ssl_client_sni_index, client_sni);
-               }
-       }
-
-       /* other cases fallback on abort, if strict-sni is set but no node was found */
-
- abort:
-       /* abort handshake (was SSL_TLSEXT_ERR_ALERT_FATAL) */
-       if (conn)
-               conn->err_code = CO_ER_SSL_HANDSHAKE;
-#ifdef OPENSSL_IS_BORINGSSL
-       return ssl_select_cert_error;
-#else
-       *al = SSL_AD_UNRECOGNIZED_NAME;
-       return 0;
-#endif
-
-allow_early:
-#ifdef OPENSSL_IS_BORINGSSL
-       if (allow_early)
-               SSL_set_early_data_enabled(ssl, 1);
-#else
-       if (!allow_early)
-               SSL_set_max_early_data(ssl, 0);
-#endif
-       return 1;
-}
-
-#else /* ! HAVE_SSL_CLIENT_HELLO_CB  */
-
-/* Sets the SSL ctx of <ssl> to match the advertised server name. Returns a
- * warning when no match is found, which implies the default (first) cert
- * will keep being used.
- */
-int ssl_sock_switchctx_cbk(SSL *ssl, int *al, void *priv)
-{
-       const char *servername;
-       const char *wildp = NULL;
-       struct ebmb_node *node, *n;
-       struct bind_conf *s = priv;
-       int default_lookup = 0; /* did we lookup for a default yet? */
-#ifdef USE_QUIC
-       const uint8_t *extension_data;
-       size_t extension_len;
-       struct quic_conn *qc = SSL_get_ex_data(ssl, ssl_qc_app_data_index);
-#endif /* USE_QUIC */
-       int i;
-       (void)al; /* shut gcc stupid warning */
-
-#ifdef USE_QUIC
-       if (qc) {
-
-               /* Look for the QUIC transport parameters. */
-               SSL_get_peer_quic_transport_params(ssl, &extension_data, &extension_len);
-               if (extension_len == 0) {
-                       /* This is not redundant. It we only return 0 without setting
-                        * <*al>, this has as side effect to generate another TLS alert
-                        * which would be set after calling quic_set_tls_alert().
-                        */
-                       *al = SSL_AD_MISSING_EXTENSION;
-                       quic_set_tls_alert(qc, SSL_AD_MISSING_EXTENSION);
-                       return SSL_TLSEXT_ERR_NOACK;
-               }
-
-               if (!quic_transport_params_store(qc, 0, extension_data,
-                                                extension_data + extension_len))
-                       return SSL_TLSEXT_ERR_NOACK;
-
-               qc->flags |= QUIC_FL_CONN_TX_TP_RECEIVED;
-       }
-#endif /* USE_QUIC */
-
-       servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
-       if (!servername) {
-#if (!defined SSL_NO_GENERATE_CERTIFICATES)
-               if (s->options & BC_O_GENERATE_CERTS && ssl_sock_generate_certificate_from_conn(s, ssl))
-                       return SSL_TLSEXT_ERR_OK;
-#endif
-               if (s->strict_sni)
-                       return SSL_TLSEXT_ERR_ALERT_FATAL;
-
-               /* without servername extension, look for the defaults which is
-                * defined by an empty servername string */
-               servername = "";
-               default_lookup = 1;
-       }
-
-sni_lookup:
-
-       for (i = 0; i < trash.size; i++) {
-               if (!servername[i])
-                       break;
-               trash.area[i] = tolower((unsigned char)servername[i]);
-               if (!wildp && (trash.area[i] == '.'))
-                       wildp = &trash.area[i];
-       }
-       trash.area[i] = 0;
-       if(!*trash.area) /* handle the default which in wildcard tree */
-               wildp = trash.area;
-
-       HA_RWLOCK_RDLOCK(SNI_LOCK, &s->sni_lock);
-       node = NULL;
-       /* lookup in full qualified names */
-       for (n = ebst_lookup(&s->sni_ctx, trash.area); n; n = ebmb_next_dup(n)) {
-               /* lookup a not neg filter */
-               if (!container_of(n, struct sni_ctx, name)->neg) {
-                       node = n;
-                       break;
-               }
-       }
-       if (!node && wildp) {
-               /* lookup in wildcards names */
-               for (n = ebst_lookup(&s->sni_w_ctx, wildp); n; n = ebmb_next_dup(n)) {
-                       /* lookup a not neg filter */
-                       if (!container_of(n, struct sni_ctx, name)->neg) {
-                               node = n;
-                               break;
-                       }
-               }
-       }
-       if (!node) {
-#if (!defined SSL_NO_GENERATE_CERTIFICATES)
-               if (s->options & BC_O_GENERATE_CERTS && ssl_sock_generate_certificate(servername, s, ssl)) {
-                       /* switch ctx done in ssl_sock_generate_certificate */
-                       HA_RWLOCK_RDUNLOCK(SNI_LOCK, &s->sni_lock);
-                       return SSL_TLSEXT_ERR_OK;
-               }
-#endif
-               HA_RWLOCK_RDUNLOCK(SNI_LOCK, &s->sni_lock);
-
-               if (!s->strict_sni && !default_lookup) {
-                       /* we didn't find a SNI, and we didn't look for a default
-                        * look again to find a matching default cert */
-                       servername = "";
-                       default_lookup = 1;
-
-                       goto sni_lookup;
-               }
-               return SSL_TLSEXT_ERR_ALERT_FATAL;
-       }
-
-#if defined(OPENSSL_IS_AWSLC)
-       /* Note that ssl_sock_switchctx_set() calls SSL_set_SSL_CTX() which propagates the
-        * "early data enabled" setting from the SSL_CTX object to the SSL objects.
-        * So enable early data for this SSL_CTX context if configured.
-        */
-       if (s->ssl_conf.early_data)
-               SSL_CTX_set_early_data_enabled(container_of(node, struct sni_ctx, name)->ctx, 1);
-#endif
-       /* switch ctx */
-       ssl_sock_switchctx_set(ssl, container_of(node, struct sni_ctx, name)->ctx);
-       HA_RWLOCK_RDUNLOCK(SNI_LOCK, &s->sni_lock);
-       return SSL_TLSEXT_ERR_OK;
-}
-#endif /* (!) OPENSSL_IS_BORINGSSL */
-
-#if defined(USE_OPENSSL_WOLFSSL)
-/* This implement the equivalent of the clientHello Callback but using the cert_cb.
- * WolfSSL is able to extract the sigalgs and ciphers of the client byt using the API
- * provided in https://github.com/wolfSSL/wolfssl/pull/6963
- *
- * Not activated for now since the PR is not merged.
- */
-static int ssl_sock_switchctx_wolfSSL_cbk(WOLFSSL* ssl, void* arg)
-{
-       struct bind_conf *s = arg;
-       int has_rsa_sig = 0, has_ecdsa_sig = 0;
-       const char *servername;
-       int default_lookup = 0;
-       struct sni_ctx *sni_ctx;
-       int i;
-
-       if (!s) {
-               /* must never happen */
-               ABORT_NOW();
-               return 0;
-       }
-
-       servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
-       if (!servername) {
-               if (s->strict_sni)
-                       goto abort;
-
-               /* without servername extension, look for the defaults which is
-                * defined by an empty servername string */
-               servername = "";
-               default_lookup = 1;
-       }
-
-       /* extract sigalgs and ciphers */
-       {
-               const byte* suites = NULL;
-               word16 suiteSz = 0;
-               const byte* hashSigAlgo = NULL;
-               word16 hashSigAlgoSz = 0;
-               word16 idx = 0;
-
-               wolfSSL_get_client_suites_sigalgs(ssl, &suites, &suiteSz, &hashSigAlgo, &hashSigAlgoSz);
-               if (suites == NULL || suiteSz == 0 || hashSigAlgo == NULL || hashSigAlgoSz == 0)
-                       return 0;
-
-               if (SSL_version(ssl) != TLS1_3_VERSION) {
-
-                       /* with TLS <= 1.2, we must use the auth which is provided by the cipher, but we don't need to
-                        * consider the auth provided by the signature algorithms */
-
-                       for (idx = 0; idx < suiteSz; idx += 2) {
-                               WOLFSSL_CIPHERSUITE_INFO info;
-                               info = wolfSSL_get_ciphersuite_info(suites[idx], suites[idx+1]);
-                               if (info.rsaAuth)
-                                       has_rsa_sig = 1;
-                               else if (info.eccAuth)
-                                       has_ecdsa_sig = 1;
-                       }
-               } else {
-                       /* with TLS >= 1.3, we must use the auth which is provided by the signature algorithms because
-                        * the ciphers does not provide the auth */
-
-                       for (idx = 0; idx < hashSigAlgoSz; idx += 2) {
-                               int hashAlgo;
-                               int sigAlgo;
-
-                               wolfSSL_get_sigalg_info(hashSigAlgo[idx+0], hashSigAlgo[idx+1], &hashAlgo, &sigAlgo);
-
-                               if (sigAlgo == RSAk || sigAlgo == RSAPSSk)
-                                       has_rsa_sig = 1;
-                               else if (sigAlgo == ECDSAk)
-                                       has_ecdsa_sig = 1;
-
-                       }
-               }
-       }
-
-sni_lookup:
-
-       /* we need to transform this into a NULL-ended string in lowecase */
-       for (i = 0; i < trash.size && servername[i] != '\0'; i++)
-               trash.area[i] = tolower(servername[i]);
-       trash.area[i] = 0;
-       servername = trash.area;
-
-       HA_RWLOCK_RDLOCK(SNI_LOCK, &s->sni_lock);
-       sni_ctx = ssl_sock_chose_sni_ctx(s, servername, has_rsa_sig, has_ecdsa_sig);
-       if (sni_ctx) {
-               /* switch ctx */
-               struct ssl_bind_conf *conf = sni_ctx->conf;
-               ssl_sock_switchctx_set(ssl, sni_ctx->ctx);
-               if (conf) {
-                       methodVersions[conf->ssl_methods.min].ssl_set_version(ssl, SET_MIN);
-                       methodVersions[conf->ssl_methods.max].ssl_set_version(ssl, SET_MAX);
-               }
-               HA_RWLOCK_RDUNLOCK(SNI_LOCK, &s->sni_lock);
-               goto allow_early;
-       }
-
-       HA_RWLOCK_RDUNLOCK(SNI_LOCK, &s->sni_lock);
-       if (!s->strict_sni && !default_lookup) {
-               /* we didn't find a SNI, and we didn't look for a default
-                * look again to find a matching default cert */
-               servername = "";
-               default_lookup = 1;
-
-               goto sni_lookup;
-       }
-
-       /* We are about to raise an handshake error so the servername extension
-        * callback will never be called and the SNI will never be stored in the
-        * SSL context. In order for the ssl_fc_sni sample fetch to still work
-        * in such a case, we store the SNI ourselves as an ex_data information
-        * in the SSL context.
-        */
-       {
-               char *client_sni = pool_alloc(ssl_sock_client_sni_pool);
-               if (client_sni) {
-                       strncpy(client_sni, servername, TLSEXT_MAXLEN_host_name);
-                       client_sni[TLSEXT_MAXLEN_host_name] = '\0';
-                       SSL_set_ex_data(ssl, ssl_client_sni_index, client_sni);
-               }
-       }
-
-       /* other cases fallback on abort, if strict-sni is set but no node was found */
-
- abort:
-       /* abort handshake (was SSL_TLSEXT_ERR_ALERT_FATAL) */
-       return 0;
-
-allow_early:
-       return 1;
-}
-#endif
-
 #ifndef OPENSSL_NO_DH
 
 static inline HASSL_DH *ssl_new_dh_fromdata(BIGNUM *p, BIGNUM *g)