From 17a0425f4f9f587e971452091a4e991573e32533 Mon Sep 17 00:00:00 2001 From: Yves Rutschle Date: Thu, 20 Aug 2026 07:56:34 +0000 Subject: [PATCH] incoming TLS connections get routed to GetEchClientHelloInner --- ChangeLog | 37 +++++++++++ configure.ac | 3 + ech_test.cfg | 45 +++++++++++++ sslh-conf.c | 78 ++++++++++++++++++----- sslh-conf.h | 7 +- sslh-main.c | 2 +- sslhconf.cfg | 6 +- tls.c | 6 ++ tls.h | 1 + tls_ech.c | 177 ++++++++++++++++++++++++++++++++++++++++++--------- 10 files changed, 309 insertions(+), 53 deletions(-) create mode 100644 ech_test.cfg diff --git a/ChangeLog b/ChangeLog index 4bd195a..27274d5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,40 @@ +ech branch: + This is an experimental version and does not work. + Do not use it unless you are experimenting with ECH + and an experimental wolfSSL. + + A lot of context is discussed here: + https://github.com/wolfSSL/wolfssl/issues/10485 + + What it does: + - It creates a public ECH configuration for the + first public name (configure in the `listen` + section of the configuration file). This gets + printed in a file configured in `ech_conf_sink` + (see ech_test.cfg example). + - Upon incoming connections, it will call an + imaginary wolfSSL_GetEchClientHelloInner(). + The resulting packet is hexdumped. + - It will also use wolfSSL to decode the SNI and + print it (but does not yet use it to forward). + + Most things happen in tls_ech.c, in particular there + is a stub of GetEchClientHelloInner() that needs to + be removed when the function becomes available in + wolfSSL. + + TODO: + Change configure script to check for appropriate + wolfSSL version. + Create ECH configurations for each public name + Link decoded InnerHello to SNI extraction code + .... + Error cases still to be worked out (see + conversations on github) + Profit! + + + v2.3.0: Added `max_connections` setting to `listen` and `protocol` configuration; see the diff --git a/configure.ac b/configure.ac index c7d7e9d..dc707c2 100644 --- a/configure.ac +++ b/configure.ac @@ -11,6 +11,9 @@ AC_CHECK_LIB([wrap], [hosts_ctl], [AC_DEFINE(HAVE_LIBWRAP) LIBS="$LIBS -lwrap" ] AC_CHECK_LIB([cap], [cap_get_proc], [AC_DEFINE(HAVE_LIBCAP) LIBS="$LIBS -lcap" ], []) AC_CHECK_LIB([bsd], [setproctitle], [AC_DEFINE(HAVE_LIBBSD) LIBS="$LIBS -lbsd" ], []) +dnl TODO: check that wolfssl contains wolfSSL_GetEchClientHelloInner instead of just existing +AC_CHECK_LIB([wolfssl], [wolfSSL_Init], [AC_DEFINE(HAVE_WOLFSSL) LIBS="$LIBS -lwolfssl" ], []) + AC_CHECK_HEADERS(linux/landlock.h, AC_DEFINE(HAVE_LANDLOCK), []) AC_CHECK_HEADERS(proxy_protocol.h, [AC_DEFINE(HAVE_PROXYPROTOCOL) LIBS="$LIBS -lproxyprotocol" ], []) diff --git a/ech_test.cfg b/ech_test.cfg new file mode 100644 index 0000000..5dc2dc2 --- /dev/null +++ b/ech_test.cfg @@ -0,0 +1,45 @@ +# sslh configuration for saaz: +# ssh to localhost +# https to nextcloud + +foreground: true; +inetd: false; +numeric: false; +transparent: false; +user: "yves"; +pidfile: "/tmp/sslh.pid"; + +syslog_facility: "auth"; + +# Where to write the ECH configurations +ech_conf_sink: "/tmp/ech"; + +# Logging configuration +# Value: 1: stdout; 2: syslog; 3: stdout+syslog; 4: logfile; ...; 7: all + + +verbose: 1; + + +# Change hostname with your external address name. +listen: +( + { host: "inet"; port: "4443"; }, + { host: "chaton"; port: "4445"; } +); + +protocols: +( + { name: "ssh"; service: "ssh"; host: "localhost"; port: "22"; fork: true; }, + { name: "tls"; sni_hostnames: [ + "inet.rutschle.net", "lists.rutschle.net" + ]; + host: "localhost"; port: "4453"; }, + { name: "tls"; sni_hostnames: [ + "chaton.rutschle.net" + ]; + host: "localhost"; port: "4455"; } +); + + +on-timeout: "ssh"; diff --git a/sslh-conf.c b/sslh-conf.c index b80d5fd..eda37e0 100644 --- a/sslh-conf.c +++ b/sslh-conf.c @@ -1,5 +1,5 @@ /* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README) - * on Wed Sep 10 15:49:49 2025. + * on Sat Jul 18 17:34:37 2026. # conf2struct: generate libconf parsers that read to structs # Copyright (C) 2018-2024 Yves Rutschle @@ -482,6 +482,8 @@ struct arg_file* sslhcfg_conffile; struct arg_str* sslhcfg_chroot; struct arg_str* sslhcfg_syslog_facility; struct arg_str* sslhcfg_logfile; + struct arg_str* sslhcfg_ech_conf_sink; + struct arg_str* sslhcfg_ech_public_name; struct arg_str* sslhcfg_on_timeout; struct arg_str* sslhcfg_prefix; struct arg_str* sslhcfg_listen; @@ -926,7 +928,7 @@ static struct config_desc table_sslhcfg_listen[] = { }, { 0 } }; - + static struct config_desc table_sslhcfg[] = { @@ -1314,6 +1316,38 @@ static struct config_desc table_sslhcfg[] = { /* default_val*/ .default_val.def_string = NULL }, + { + /* name */ "ech_conf_sink", + /* type */ CFG_STRING, + /* sub_group*/ NULL, + /* arg_cl */ & sslhcfg_ech_conf_sink, + /* base_addr */ NULL, + /* offset */ offsetof(struct sslhcfg_item, ech_conf_sink), + /* offset_len */ 0, + /* offset_present */ offsetof(struct sslhcfg_item, ech_conf_sink_is_present), + /* size */ sizeof(char*), + /* array_type */ -1, + /* mandatory */ 0, + /* optional */ 1, + /* default_val*/ .default_val.def_string = NULL + }, + + { + /* name */ "ech_public_name", + /* type */ CFG_STRING, + /* sub_group*/ NULL, + /* arg_cl */ & sslhcfg_ech_public_name, + /* base_addr */ NULL, + /* offset */ offsetof(struct sslhcfg_item, ech_public_name), + /* offset_len */ 0, + /* offset_present */ offsetof(struct sslhcfg_item, ech_public_name_is_present), + /* size */ sizeof(char*), + /* array_type */ -1, + /* mandatory */ 0, + /* optional */ 1, + /* default_val*/ .default_val.def_string = NULL + }, + { /* name */ "on_timeout", /* type */ CFG_STRING, @@ -1500,7 +1534,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: listen */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_listen, - .base_entry = & table_sslhcfg [26], + .base_entry = & table_sslhcfg [28], .targets = sslhcfg_listen_targets, @@ -1512,7 +1546,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: ssh */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_ssh, - .base_entry = & table_sslhcfg [27], + .base_entry = & table_sslhcfg [29], .targets = sslhcfg_ssh_targets, @@ -1524,7 +1558,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: tls */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_tls, - .base_entry = & table_sslhcfg [27], + .base_entry = & table_sslhcfg [29], .targets = sslhcfg_tls_targets, @@ -1536,7 +1570,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: ssl */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_ssl, - .base_entry = & table_sslhcfg [27], + .base_entry = & table_sslhcfg [29], .targets = sslhcfg_ssl_targets, @@ -1548,7 +1582,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: openvpn */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_openvpn, - .base_entry = & table_sslhcfg [27], + .base_entry = & table_sslhcfg [29], .targets = sslhcfg_openvpn_targets, @@ -1560,7 +1594,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: tinc */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_tinc, - .base_entry = & table_sslhcfg [27], + .base_entry = & table_sslhcfg [29], .targets = sslhcfg_tinc_targets, @@ -1572,7 +1606,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: wireguard */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_wireguard, - .base_entry = & table_sslhcfg [27], + .base_entry = & table_sslhcfg [29], .targets = sslhcfg_wireguard_targets, @@ -1584,7 +1618,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: xmpp */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_xmpp, - .base_entry = & table_sslhcfg [27], + .base_entry = & table_sslhcfg [29], .targets = sslhcfg_xmpp_targets, @@ -1596,7 +1630,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: http */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_http, - .base_entry = & table_sslhcfg [27], + .base_entry = & table_sslhcfg [29], .targets = sslhcfg_http_targets, @@ -1608,7 +1642,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: adb */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_adb, - .base_entry = & table_sslhcfg [27], + .base_entry = & table_sslhcfg [29], .targets = sslhcfg_adb_targets, @@ -1620,7 +1654,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: socks5 */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_socks5, - .base_entry = & table_sslhcfg [27], + .base_entry = & table_sslhcfg [29], .targets = sslhcfg_socks5_targets, @@ -1632,7 +1666,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: syslog */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_syslog, - .base_entry = & table_sslhcfg [27], + .base_entry = & table_sslhcfg [29], .targets = sslhcfg_syslog_targets, @@ -1644,7 +1678,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: msrdp */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_msrdp, - .base_entry = & table_sslhcfg [27], + .base_entry = & table_sslhcfg [29], .targets = sslhcfg_msrdp_targets, @@ -1656,7 +1690,7 @@ static struct compound_cl_arg compound_cl_args[] = { { /* arg: anyprot */ .regex = "(.+):(\\w+)", .arg_cl = & sslhcfg_anyprot, - .base_entry = & table_sslhcfg [27], + .base_entry = & table_sslhcfg [29], .targets = sslhcfg_anyprot_targets, @@ -2345,6 +2379,8 @@ int sslhcfg_cl_parse(int argc, char* argv[], struct sslhcfg_item* cfg) sslhcfg_chroot = arg_strn("C", "chroot", "", 0, 1, "Root to change to after set-up"), sslhcfg_syslog_facility = arg_strn(NULL, "syslog-facility", "", 0, 1, "Facility to syslog to"), sslhcfg_logfile = arg_strn(NULL, "logfile", "", 0, 1, "Log messages to a file"), + sslhcfg_ech_conf_sink = arg_strn(NULL, "ech-conf-sink", "", 0, 1, "File where to write ECH configurations"), + sslhcfg_ech_public_name = arg_strn(NULL, "ech-public-name", "", 0, 1, "Public name for ECH client-facing proxy (usually, sslh's address)"), sslhcfg_on_timeout = arg_strn(NULL, "on-timeout", "", 0, 1, "Target to connect to when timing out"), sslhcfg_prefix = arg_strn(NULL, "prefix", "", 0, 1, "Reserved for testing"), sslhcfg_listen = arg_strn("p", "listen", "", 0, 10, "Listen on host:port"), @@ -2621,6 +2657,16 @@ void sslhcfg_fprint( fprintf(out, " "); fprintf(out, "\n"); indent(out, depth); + fprintf(out, "ech_conf_sink: %s", sslhcfg->ech_conf_sink); + if (! sslhcfg->ech_conf_sink_is_present) + fprintf(out, " "); + fprintf(out, "\n"); + indent(out, depth); + fprintf(out, "ech_public_name: %s", sslhcfg->ech_public_name); + if (! sslhcfg->ech_public_name_is_present) + fprintf(out, " "); + fprintf(out, "\n"); + indent(out, depth); fprintf(out, "on_timeout: %s", sslhcfg->on_timeout); fprintf(out, "\n"); indent(out, depth); diff --git a/sslh-conf.h b/sslh-conf.h index f5d8be3..01a4e2a 100644 --- a/sslh-conf.h +++ b/sslh-conf.h @@ -1,5 +1,5 @@ /* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README) - * on Wed Sep 10 15:49:49 2025. + * on Sat Jul 18 17:34:37 2026. # conf2struct: generate libconf parsers that read to structs # Copyright (C) 2018-2024 Yves Rutschle @@ -38,6 +38,7 @@ #include "probe.h" #include #include +#include #include struct sslhcfg_listen_item { @@ -114,6 +115,10 @@ struct sslhcfg_item { char* syslog_facility; int logfile_is_present; char* logfile; + int ech_conf_sink_is_present; + char* ech_conf_sink; + int ech_public_name_is_present; + char* ech_public_name; char* on_timeout; char* prefix; size_t listen_len; diff --git a/sslh-main.c b/sslh-main.c index 1e3eef1..28535dd 100644 --- a/sslh-main.c +++ b/sslh-main.c @@ -347,7 +347,7 @@ int main(int argc, char *argv[], char* envp[]) /* Open log file for writing */ setup_logfile(); - ech_init(); + ech_init(listen_sockets, num_addr_listen); if (cfg.user || cfg.chroot) drop_privileges(cfg.user, cfg.chroot); diff --git a/sslhconf.cfg b/sslhconf.cfg index 6ae6818..aba6aaa 100644 --- a/sslhconf.cfg +++ b/sslhconf.cfg @@ -86,6 +86,8 @@ config: { description: "Facility to syslog to"; }, { name: "logfile"; type: "string"; optional: true; description: "Log messages to a file" }, + { name: "ech_conf_sink"; type: "string"; optional: true; + description: "File where to write ECH configurations" }, { name: "on-timeout"; type: "string"; default: "ssh"; description: "Target to connect to when timing out"; }, @@ -131,10 +133,6 @@ 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.c b/tls.c index 5a0e64a..591cc22 100644 --- a/tls.c +++ b/tls.c @@ -61,6 +61,7 @@ static int parse_server_name_extension(const struct TLSProtocol *, const char *, static int parse_alpn_extension(const struct TLSProtocol *, const char *, size_t); static int has_match(const char**, size_t, const char*, size_t); + /* Parse a TLS packet for the Server Name Indication and ALPN extension in the client * hello handshake, returning a status code * @@ -81,6 +82,11 @@ parse_tls_header(const struct TLSProtocol *tls_data, const char *data, size_t da if (data_len < TLS_HEADER_LEN) return TLS_ELENGTH; + /* If if it is an ECH, decrypt and match */ + ech_match_sni(data, data_len); + + + tls_content_type = data[0]; if (tls_content_type != TLS_HANDSHAKE_CONTENT_TYPE) { print_message(msg_probe_info, "Request did not begin with TLS handshake.\n"); diff --git a/tls.h b/tls.h index c6cf1a7..b35f651 100644 --- a/tls.h +++ b/tls.h @@ -44,5 +44,6 @@ struct TLSProtocol *tls_data_set_list(struct TLSProtocol *, int, const char**, s #define TLS_ENOEXT -4 /* No ALPN or SNI extension found */ #define TLS_EPROTOCOL -5 /* Protocol error */ +void ech_match_sni(const char* buffer_in, size_t buffer_len); #endif diff --git a/tls_ech.c b/tls_ech.c index 9cb10c5..50116ac 100644 --- a/tls_ech.c +++ b/tls_ech.c @@ -33,34 +33,134 @@ #include +#include "config.h" #include "log.h" #include "sslh-conf.h" +#include "tls.h" + +WOLFSSL_CTX* wolfssl_ctx; -void ech_get_client_hello_inner(const char* data, size_t datalen) +// Hard-coded server certificate (PEM format) +// from snakeoil -- these serves no purpose beyond initialising the context +static const char* SERVER_CERT_PEM = +"-----BEGIN CERTIFICATE-----\n" +"MIICyjCCAbKgAwIBAgIUZrCzD4tEIu1r9EhkOn96raiupe4wDQYJKoZIhvcNAQEL\n" +"BQAwDzENMAsGA1UEAwwEc2FhejAeFw0yMTA1MjgyMDM0MjRaFw0zMTA1MjYyMDM0\n" +"MjRaMA8xDTALBgNVBAMMBHNhYXowggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\n" +"AoIBAQC5s1UhjcmsrRnSdJMH82NxRQ3hlwMxGZz5NnI0MukXN5kptAYSge4BQc6J\n" +"MAL1MbPtX0pfR+yBILWUdn6JCnDqhv9ipO8OpeVeh+RKa2kGiEdeoQhBbKNZdsoe\n" +"gD9FE3lE895GIhEv7Jd9bt/r8CcCpmOLuhJF6md0cLWGPrAp4l+iCV/ixyE0z0bK\n" +"fJzuOxi7oIeINLEiR0Z4hJsM2XAEXlBEsI4oh2KdDJiU6ow3w25IY7+/nYVDDLD2\n" +"QFMByfkEmAGp9Br+2tquRUmR8qmSvZPMtWpOzMv8+iKTrxUcAUCtCpDMjHqNR8fd\n" +"6G6TLenPbQPEaac5UgGZ7rJvL9idAgMBAAGjHjAcMAkGA1UdEwQCMAAwDwYDVR0R\n" +"BAgwBoIEc2FhejANBgkqhkiG9w0BAQsFAAOCAQEAZPasi1Hu7q4S9wJsbIzcGj7m\n" +"ZCAxNdm3AINeV3mxfhToeqtXSoa2sujCEgwCuiYi0OE8wmseLO8e1xIsRc/NEpOl\n" +"h9u5OupV2Xbf7nuKzPl31YQ7kv+90mnixizf6CFpHXAdU3WEZ33+fMtS+lV/ZBYo\n" +"Nh9GGNJ7Grv5KIKu/b2cR403pRF1hGrjWABrIDZO3ZCtNgRp/My1Qr+/04rTs8VF\n" +"WyDshOe0u6Xo//M3sCMbx5+62BopCMmAu2TxiPew0OTM8eDa3IBbc2kLGxp8c4J+\n" +"EYpAKTZbkhs/6SleNMC4BOEmZQ1tk6D0N0u4Y0b1IKEjgx7HqwZ6mIdmbU1lZQ==\n" +"-----END CERTIFICATE-----\n"; + +static const char* SERVER_KEY_PEM = +"-----BEGIN PRIVATE KEY-----\n" +"MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC5s1UhjcmsrRnS\n" +"dJMH82NxRQ3hlwMxGZz5NnI0MukXN5kptAYSge4BQc6JMAL1MbPtX0pfR+yBILWU\n" +"dn6JCnDqhv9ipO8OpeVeh+RKa2kGiEdeoQhBbKNZdsoegD9FE3lE895GIhEv7Jd9\n" +"bt/r8CcCpmOLuhJF6md0cLWGPrAp4l+iCV/ixyE0z0bKfJzuOxi7oIeINLEiR0Z4\n" +"hJsM2XAEXlBEsI4oh2KdDJiU6ow3w25IY7+/nYVDDLD2QFMByfkEmAGp9Br+2tqu\n" +"RUmR8qmSvZPMtWpOzMv8+iKTrxUcAUCtCpDMjHqNR8fd6G6TLenPbQPEaac5UgGZ\n" +"7rJvL9idAgMBAAECggEAbXNQctViD596/7rryhze+KztsI/UNsVU6uH3T+LN1XxU\n" +"jurnRVNFV4JU3DDrHV9tIDQw3pXCxJzlyRpKHDWGNgTZU2fI6sZGuX+4w1Apd8ss\n" +"3Y5mEkGYyaslF/MY13rR2B0wXf/IR4nYA/ssiaBjLcGqHHnQOVseHNgtdXFPXW9n\n" +"5IXJ2PSeaFW0Z13vbTQm9Cx2kCBKL4J3TymhkuSW3DUHxmlDfWUK14lkyXCZIZ3i\n" +"TbXQAXhwTrz3wqbG++iEP4MZM0Ka7/+WXqrsxA/yCirb+HnMewxwBKzGvkym2I2H\n" +"3BkjDn9exDgTePD7i0w22UYCF5uezDB9YOiPVZ634QKBgQDyKIgxD7XMMJezeg0Y\n" +"PaG+Eig/hE27mm2pyTQSd6MMZ1HpbDUpLQ/GO3Npy+bKS0/5eefNT4PWVcmpRL4W\n" +"blNIfixkcZq1a6wax9Nhk89jXZdhBinO8gfXFOiANFDS7B4fEXeTrfhmuryqUw9m\n" +"L6GJ0jk/H6fs6ads15y+O7A6tQKBgQDEUKlZg4qXxNMNKq7l+v7R6IH8VrNLt0I/\n" +"f4fhpW3IhmPJCRuOFOS0UpDPYjTeu14i3EFcpe0D+DVoQiggBTAh3I/qMHvRko5g\n" +"DT83YHp2/ec0AY0ocwnLwye224t6GF7S8R8vhG1FkIBk6DTJgWFZLDtY+dekZugz\n" +"IHvgtxSPSQKBgHWD7fPBJ/xbaIMUq94jqqZOsXGBhyePncBTgA2mOV3/leStOm8t\n" +"CwasOyoQZYOuYLU0z6T2/Ye6Qg7+6TCBgbEgafKknuuDwRWN+6rSzEXwWVIgZ5Gi\n" +"KuPZparxuHdjVorFMz5borxXys7tV//DBaWYe2eCuT9jdHiBefNni/IxAoGAWHTn\n" +"+deVqVEcsAAdkq8IBtk3SNZgL6vBhA8Y0QnTb9luOPWLnve3HbFeYrOjkwDmJ9sK\n" +"8I4rP/ClT/cPUW5FA9z4U9PI1uOsl4cghvlH9Tnu5bYVPranIVMsH+7I6Bj3ESFo\n" +"peaLvh6gW9dtgaZ0kNOnYi4hhoh/9Bmc9+JGt1ECgYAbGSQLa21L4Hx3NvMparVQ\n" +"bGjN26igvqKmhBaKrH8YolOlhEw/mCwTlg9zI7jrhdO0VClPrB8IVBoL+6HvrT21\n" +"LJM31J3ddqME9/5fQgKMQsD60kRDD6iV/iK2S6TOUjxJn1f6MuLmGSOHZYcbdBPZ\n" +"DyBKgbz/xGV6katjXvg74A==\n" +"-----END PRIVATE KEY-----\n"; + + + +#if 1 +/* Will be provided by wolfSSL */ +int wolfSSL_GetEchClientHelloInner( + WOLFSSL* ssl, /* Connected to an existing context with the ECH configs. Stores hpke state */ + const byte* buffer_in, /* The full ClientHelloOuter */ + word32 buffer_len, /* How many bytes in the buffer */ + byte* buffer_out, + word32* buffer_out_len + ) +{ + char* inner = "some clienthelloinner"; + + *buffer_out_len = strlen(inner); + memcpy(buffer_out, inner, *buffer_out_len); + print_message(msg_fd, "GetEchClientHelloInner set\n"); + return 0; +} +#endif + + +void ech_get_client_hello_inner(WOLFSSL_CTX* ctx, const byte* data, size_t datalen) { -#if 0 - WOLFSSL_CTX* ctx = NULL; WOLFSSL* ssl = NULL; + printf("wolfSSL_new(%p)\n", ctx); 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); + byte client_hello_inner[1024]; + word32 ch_len = sizeof(client_hello_inner);; - char* sni_data = NULL; - word16 sni_len = wolfSSL_SNI_GetRequest(ssl, WOLFSSL_SNI_HOST_NAME, (void**)&sni_data); -#endif + int res = wolfSSL_GetEchClientHelloInner( + ssl, + data, /* The full ClientHelloOuter */ + datalen, /* How many bytes in the buffer */ + (byte*)&client_hello_inner, + &ch_len + ); + client_hello_inner[ch_len] = 0; + if (!res) { + print_message(msg_fd, "GetEchClientHelloInner: Innerlen: %d\n", ch_len); + hexdump(msg_fd, (char*)client_hello_inner, ch_len); + print_message(msg_fd, "/GetEchClientHelloInner\n"); + } + + + byte sni_data[1024]; + unsigned int sni_len = sizeof(sni_data); + memset(sni_data, 'A', sni_len); + + printf("SNI_GetFromBuffer\n"); + wolfSSL_SNI_GetFromBuffer(data, datalen, 0, (byte*)&sni_data, &sni_len); + sni_data[sni_len] = 0; + printf("sni_len = %d\n", sni_len); + printf("sni: %s\n", sni_data); } -void ech_match_sni(void) +/* See if the context matches */ +void ech_match_sni(const char* buffer_in, size_t buffer_len) { + ech_get_client_hello_inner(wolfssl_ctx, (byte*)buffer_in, buffer_len); } @@ -90,8 +190,6 @@ static void reset_conf(void) print_message(msg_config_error, "Unable to open %s:%d:%s\n", cfg.ech_conf_sink, errno, strerror(errno)); return; } - - printf("erased and opend %s\n", cfg.ech_conf_sink); } @@ -103,6 +201,8 @@ static void ech_genkey(WOLFSSL_CTX* ctx, const char* sni) { int ret; + printf("ech_genkey for ctx %p\n", ctx); + 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) { @@ -119,7 +219,7 @@ static void ech_genkey(WOLFSSL_CTX* ctx, const char* sni) } char out[512]; - int outLen = sizeof(out); + word32 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); @@ -132,38 +232,53 @@ static void ech_genkey(WOLFSSL_CTX* ctx, const char* sni) * (Later, this will load the configurations from some place else */ static void ech_create_configs(WOLFSSL_CTX** ctx) { - int i, j; + int ret; reset_conf(); - for (i = 0; i < cfg.listen_len; i++) { - struct sslhcfg_protocols_item* listen = &cfg.listen[i]; - printf("1\n"); - *ctx = malloc(cfg.listen_len * sizeof(*ctx)); - /* TODO deal with malloc failure */ - printf("2\n"); - - ctx[i] = wolfSSL_CTX_new(wolfTLSv1_3_server_method()); - if (!ctx[i]) { - print_message(msg_system_error, "Failed to create WOLFSSL_CTX\n"); - exit(1); - } - - printf("3\n"); - ech_genkey(ctx[i], cfg.listen[i].host); - printf("4\n"); + *ctx = wolfSSL_CTX_new(wolfTLSv1_3_server_method()); + if (!*ctx) { + print_message(msg_system_error, "Failed to create WOLFSSL_CTX\n"); + exit(1); } + + // Load certificate from memory + ret = wolfSSL_CTX_use_certificate_buffer(*ctx, + (const unsigned char*)SERVER_CERT_PEM, + strlen(SERVER_CERT_PEM), + WOLFSSL_FILETYPE_PEM); + if (ret != WOLFSSL_SUCCESS) { + fprintf(stderr, "Failed to load certificate: %d\n", ret); + fprintf(stderr, "Please replace SERVER_CERT_PEM with a valid certificate\n"); + wolfSSL_CTX_free(*ctx); + exit(1); + } + + // Load private key from memory + ret = wolfSSL_CTX_use_PrivateKey_buffer(*ctx, + (const unsigned char*)SERVER_KEY_PEM, + strlen(SERVER_KEY_PEM), + WOLFSSL_FILETYPE_PEM); + if (ret != WOLFSSL_SUCCESS) { + fprintf(stderr, "Failed to load private key: %d\n", ret); + fprintf(stderr, "Please replace SERVER_KEY_PEM with a valid private key\n"); + wolfSSL_CTX_free(*ctx); + exit(1); + } + + printf("Certificate and key loaded successfully\n"); + ech_genkey(*ctx, cfg.listen[0].host); /* For now this only works on the first host */ } + /* Initialises the ECH subsystem: create wolfSSL context, create ECH configs, * etc */ + void ech_init() { - WOLFSSL_CTX* ctx = NULL; - wolfSSL_Init(); - ech_create_configs(&ctx); + ech_create_configs(&wolfssl_ctx); }