From: William Lallemand Date: Thu, 13 Jun 2024 14:45:48 +0000 (+0200) Subject: REORG: ssl: move the SNI selection code in ssl_clienthello.c X-Git-Tag: v3.1-dev1~10 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=4ced880d22c67e83d5e134fdc13ec65606fbf21a;p=thirdparty%2Fhaproxy.git REORG: ssl: move the SNI selection code in ssl_clienthello.c 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 --- diff --git a/Makefile b/Makefile index cefee0f5df..ab3f3b1d37 100644 --- 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=),) diff --git a/include/haproxy/ssl_sock-t.h b/include/haproxy/ssl_sock-t.h index d111883ddd..a8c37e6bf2 100644 --- a/include/haproxy/ssl_sock-t.h +++ b/include/haproxy/ssl_sock-t.h @@ -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 index 0000000000..4afa5df94c --- /dev/null +++ b/src/ssl_clienthello.c @@ -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 +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +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 and a chosen (must be in lowercase) + * RSA and ECDSA 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 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: + */ diff --git a/src/ssl_sock.c b/src/ssl_sock.c index 8bd6099942..87ee42f1a7 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -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 and a chosen (must be in lowercase) - * RSA and ECDSA 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 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)