diff --git a/Makefile.in b/Makefile.in index dcd6012..72f163d 100644 --- a/Makefile.in +++ b/Makefile.in @@ -35,7 +35,7 @@ CFLAGS +=-Wall -O2 -DLIBPCRE -g $(CFLAGS_COV) $(CFLAGS_SAN) LIBS=-lm -lpcre2-8 @LIBS@ -OBJS=sslh-conf.o common.o log.o sslh-main.o probe.o tls.o argtable3.o collection.o gap.o tcp-probe.o landlock.o proxyprotocol.o +OBJS=sslh-conf.o common.o log.o sslh-main.o probe.o tls.o tls_ech.o argtable3.o collection.o gap.o tcp-probe.o landlock.o proxyprotocol.o OBJS_A=libsslh.a FORK_OBJS=sslh-fork.o $(OBJS_A) SELECT_OBJS=processes.o udp-listener.o sslh-select.o hash.o tcp-listener.o $(OBJS_A) diff --git a/config.h.in b/config.h.in index 983a8c7..8b9bc01 100644 --- a/config.h.in +++ b/config.h.in @@ -18,4 +18,7 @@ /* libbsd, to change process name */ #undef HAVE_LIBBSD +/* wolfssl, to support Encrypted Client Hello (ECH) */ +#undef HAVE_WOLFSSL + #endif diff --git a/configure b/configure index 789c55e..9419a0e 100755 --- a/configure +++ b/configure @@ -3329,6 +3329,47 @@ then : LIBS="$LIBS -lbsd" fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for wolfSSL_Init in -lwolfssl" >&5 +printf %s "checking for wolfSSL_Init in -lwolfssl... " >&6; } +if test ${ac_cv_lib_wolfssl_wolfSSL_Init+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_check_lib_save_LIBS=$LIBS +LIBS="-lwolfssl $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +char wolfSSL_Init (); +int +main (void) +{ +return wolfSSL_Init (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO" +then : + ac_cv_lib_wolfssl_wolfSSL_Init=yes +else $as_nop + ac_cv_lib_wolfssl_wolfSSL_Init=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_wolfssl_wolfSSL_Init" >&5 +printf "%s\n" "$ac_cv_lib_wolfssl_wolfSSL_Init" >&6; } +if test "x$ac_cv_lib_wolfssl_wolfSSL_Init" = xyes +then : + printf "%s\n" "#define HAVE_WOLFSSL 1" >>confdefs.h + LIBS="$LIBS -lwolfssl" +fi + ac_header= ac_cache= for ac_item in $ac_header_c_list diff --git a/sslh-main.c b/sslh-main.c index 94cd6c6..1e3eef1 100644 --- a/sslh-main.c +++ b/sslh-main.c @@ -347,6 +347,8 @@ int main(int argc, char *argv[], char* envp[]) /* Open log file for writing */ setup_logfile(); + ech_init(); + if (cfg.user || cfg.chroot) drop_privileges(cfg.user, cfg.chroot); setup_landlock(); diff --git a/sslhconf.cfg b/sslhconf.cfg index 964393c..6ae6818 100644 --- a/sslhconf.cfg +++ b/sslhconf.cfg @@ -18,6 +18,7 @@ includes: ( "probe.h", " ", " ", + " ", " " ); @@ -130,6 +131,10 @@ config: { type: "array", element_type: "string" }, + { name: "wolfssl_ctx", # One context object per public hostname; they will have the same index as prot->sni_hostnames + type: "runtime", + c_type: "WOLFSSL_CTX**" + }, { name: "alpn_protocols", type: "array", element_type: "string" diff --git a/tls_ech.c b/tls_ech.c new file mode 100644 index 0000000..5a09dd4 --- /dev/null +++ b/tls_ech.c @@ -0,0 +1,132 @@ +/* +# tls_ech.c: support for RFC9849 TLS Encrypted Client Hello (ECH) +# +# Copyright (C) 2026 Yves Rutschle +# +# This program is free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied +# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +# PURPOSE. See the GNU General Public License for more +# details. +# +# The full text for the General Public License is here: +# http://www.gnu.org/licenses/gpl.html + +*/ + +#define WOLFSSL_USE_OPTION_H +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "log.h" +#include "sslh-conf.h" + + +void ech_get_client_hello_inner(const char* data, size_t datalen) +{ +#if 0 + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + + ssl = wolfSSL_new(ctx); + if (ssl == NULL) { + fprintf(stderr, "Failed to create WOLFSSL object\n"); + close(client_fd); + return; + } + + // Disable certificate verification on this connection + wolfSSL_set_verify(ssl, WOLFSSL_VERIFY_NONE, NULL); + + + char* sni_data = NULL; + word16 sni_len = wolfSSL_SNI_GetRequest(ssl, WOLFSSL_SNI_HOST_NAME, (void**)&sni_data); +#endif +} + +void ech_match_sni(void) +{ +} + +/* Generate a keypair for a config; later this will be moved to a separate + * process, and sslh will just read it */ +static void ech_genkey(WOLFSSL_CTX* ctx, const char* sni) +{ + int ret; + + print_message(msg_config, "Generating ECH config for outer SNI: %s\n", sni); + ret = wolfSSL_CTX_GenerateEchConfig(ctx, sni, 0, 0, 0); + if (ret != WOLFSSL_SUCCESS) { + print_message(msg_system_error, "Failed to generate ECH config: %d\n", ret); + exit(1); + } + + byte echConfig[512]; + word32 echConfigLen = sizeof(echConfig); + ret = wolfSSL_CTX_GetEchConfigs(ctx, echConfig, &echConfigLen); + if (ret != WOLFSSL_SUCCESS) { + print_message(msg_system_error, "Failed to get ECH config: %d\n", ret); + exit(1); + } + + char out[512]; + int outLen = sizeof(out); + if (Base64_Encode_NoNl(echConfig, echConfigLen, (byte*)out, &outLen) != 0) { + print_message(msg_system_error, "Failed to encode to base64: %d\n", ret); + exit(1); + } + + print_message(msg_config, "%s. IN HTTPS 1 . alpn=\"h2\" ech=\"%s\"\n", sni, out); + +} + + +/* For all server names, create a keypair and configuration. + * (Later, this will load the configurations from some place else */ +static void ech_load_configs(WOLFSSL_CTX* ctx) +{ + int i, j; + + for (i = 0; i < cfg.protocols_len; i++) { + struct sslhcfg_protocols_item* prot = &cfg.protocols[i]; + prot->wolfssl_ctx = malloc(prot->sni_hostnames_len * sizeof(prot->wolfssl_ctx[0])); + /* TODO deal with malloc failure */ + + for (j = 0; j < prot->sni_hostnames_len; j++) { + prot->wolfssl_ctx[j] = wolfSSL_CTX_new(wolfTLSv1_3_server_method()); + if (prot->wolfssl_ctx[j] == NULL) { + print_message(msg_system_error, "Failed to create WOLFSSL_CTX\n"); + exit(1); + } + + ech_genkey(prot->wolfssl_ctx[j], prot->sni_hostnames[j]); + } + } + +} + + +/* Initialises the ECH subsystem: create wolfSSL context, create ECH configs, + * etc */ +void ech_init() +{ + + wolfSSL_Init(); + + ech_load_configs(NULL); +}