mirror of
https://github.com/yrutschle/sslh.git
synced 2026-10-22 10:02:09 +03:00
incoming TLS connections get routed to GetEchClientHelloInner
This commit is contained in:
1 parent
34645a62b2
commit
17a0425f4f
10 files changed
+308
-52
No files matched your search
@@ -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:
|
v2.3.0:
|
||||||
Added `max_connections` setting to `listen` and
|
Added `max_connections` setting to `listen` and
|
||||||
`protocol` configuration; see the
|
`protocol` configuration; see the
|
||||||
|
|||||||
@@ -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([cap], [cap_get_proc], [AC_DEFINE(HAVE_LIBCAP) LIBS="$LIBS -lcap" ], [])
|
||||||
AC_CHECK_LIB([bsd], [setproctitle], [AC_DEFINE(HAVE_LIBBSD) LIBS="$LIBS -lbsd" ], [])
|
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(linux/landlock.h, AC_DEFINE(HAVE_LANDLOCK), [])
|
||||||
AC_CHECK_HEADERS(proxy_protocol.h, [AC_DEFINE(HAVE_PROXYPROTOCOL) LIBS="$LIBS -lproxyprotocol" ], [])
|
AC_CHECK_HEADERS(proxy_protocol.h, [AC_DEFINE(HAVE_PROXYPROTOCOL) LIBS="$LIBS -lproxyprotocol" ], [])
|
||||||
|
|
||||||
|
|||||||
@@ -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";
|
||||||
+61
-15
@@ -1,5 +1,5 @@
|
|||||||
/* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README)
|
/* 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
|
# conf2struct: generate libconf parsers that read to structs
|
||||||
# Copyright (C) 2018-2024 Yves Rutschle
|
# Copyright (C) 2018-2024 Yves Rutschle
|
||||||
@@ -482,6 +482,8 @@ struct arg_file* sslhcfg_conffile;
|
|||||||
struct arg_str* sslhcfg_chroot;
|
struct arg_str* sslhcfg_chroot;
|
||||||
struct arg_str* sslhcfg_syslog_facility;
|
struct arg_str* sslhcfg_syslog_facility;
|
||||||
struct arg_str* sslhcfg_logfile;
|
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_on_timeout;
|
||||||
struct arg_str* sslhcfg_prefix;
|
struct arg_str* sslhcfg_prefix;
|
||||||
struct arg_str* sslhcfg_listen;
|
struct arg_str* sslhcfg_listen;
|
||||||
@@ -1314,6 +1316,38 @@ static struct config_desc table_sslhcfg[] = {
|
|||||||
/* default_val*/ .default_val.def_string = NULL
|
/* 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",
|
/* name */ "on_timeout",
|
||||||
/* type */ CFG_STRING,
|
/* type */ CFG_STRING,
|
||||||
@@ -1500,7 +1534,7 @@ static struct compound_cl_arg compound_cl_args[] = {
|
|||||||
{ /* arg: listen */
|
{ /* arg: listen */
|
||||||
.regex = "(.+):(\\w+)",
|
.regex = "(.+):(\\w+)",
|
||||||
.arg_cl = & sslhcfg_listen,
|
.arg_cl = & sslhcfg_listen,
|
||||||
.base_entry = & table_sslhcfg [26],
|
.base_entry = & table_sslhcfg [28],
|
||||||
.targets = sslhcfg_listen_targets,
|
.targets = sslhcfg_listen_targets,
|
||||||
|
|
||||||
|
|
||||||
@@ -1512,7 +1546,7 @@ static struct compound_cl_arg compound_cl_args[] = {
|
|||||||
{ /* arg: ssh */
|
{ /* arg: ssh */
|
||||||
.regex = "(.+):(\\w+)",
|
.regex = "(.+):(\\w+)",
|
||||||
.arg_cl = & sslhcfg_ssh,
|
.arg_cl = & sslhcfg_ssh,
|
||||||
.base_entry = & table_sslhcfg [27],
|
.base_entry = & table_sslhcfg [29],
|
||||||
.targets = sslhcfg_ssh_targets,
|
.targets = sslhcfg_ssh_targets,
|
||||||
|
|
||||||
|
|
||||||
@@ -1524,7 +1558,7 @@ static struct compound_cl_arg compound_cl_args[] = {
|
|||||||
{ /* arg: tls */
|
{ /* arg: tls */
|
||||||
.regex = "(.+):(\\w+)",
|
.regex = "(.+):(\\w+)",
|
||||||
.arg_cl = & sslhcfg_tls,
|
.arg_cl = & sslhcfg_tls,
|
||||||
.base_entry = & table_sslhcfg [27],
|
.base_entry = & table_sslhcfg [29],
|
||||||
.targets = sslhcfg_tls_targets,
|
.targets = sslhcfg_tls_targets,
|
||||||
|
|
||||||
|
|
||||||
@@ -1536,7 +1570,7 @@ static struct compound_cl_arg compound_cl_args[] = {
|
|||||||
{ /* arg: ssl */
|
{ /* arg: ssl */
|
||||||
.regex = "(.+):(\\w+)",
|
.regex = "(.+):(\\w+)",
|
||||||
.arg_cl = & sslhcfg_ssl,
|
.arg_cl = & sslhcfg_ssl,
|
||||||
.base_entry = & table_sslhcfg [27],
|
.base_entry = & table_sslhcfg [29],
|
||||||
.targets = sslhcfg_ssl_targets,
|
.targets = sslhcfg_ssl_targets,
|
||||||
|
|
||||||
|
|
||||||
@@ -1548,7 +1582,7 @@ static struct compound_cl_arg compound_cl_args[] = {
|
|||||||
{ /* arg: openvpn */
|
{ /* arg: openvpn */
|
||||||
.regex = "(.+):(\\w+)",
|
.regex = "(.+):(\\w+)",
|
||||||
.arg_cl = & sslhcfg_openvpn,
|
.arg_cl = & sslhcfg_openvpn,
|
||||||
.base_entry = & table_sslhcfg [27],
|
.base_entry = & table_sslhcfg [29],
|
||||||
.targets = sslhcfg_openvpn_targets,
|
.targets = sslhcfg_openvpn_targets,
|
||||||
|
|
||||||
|
|
||||||
@@ -1560,7 +1594,7 @@ static struct compound_cl_arg compound_cl_args[] = {
|
|||||||
{ /* arg: tinc */
|
{ /* arg: tinc */
|
||||||
.regex = "(.+):(\\w+)",
|
.regex = "(.+):(\\w+)",
|
||||||
.arg_cl = & sslhcfg_tinc,
|
.arg_cl = & sslhcfg_tinc,
|
||||||
.base_entry = & table_sslhcfg [27],
|
.base_entry = & table_sslhcfg [29],
|
||||||
.targets = sslhcfg_tinc_targets,
|
.targets = sslhcfg_tinc_targets,
|
||||||
|
|
||||||
|
|
||||||
@@ -1572,7 +1606,7 @@ static struct compound_cl_arg compound_cl_args[] = {
|
|||||||
{ /* arg: wireguard */
|
{ /* arg: wireguard */
|
||||||
.regex = "(.+):(\\w+)",
|
.regex = "(.+):(\\w+)",
|
||||||
.arg_cl = & sslhcfg_wireguard,
|
.arg_cl = & sslhcfg_wireguard,
|
||||||
.base_entry = & table_sslhcfg [27],
|
.base_entry = & table_sslhcfg [29],
|
||||||
.targets = sslhcfg_wireguard_targets,
|
.targets = sslhcfg_wireguard_targets,
|
||||||
|
|
||||||
|
|
||||||
@@ -1584,7 +1618,7 @@ static struct compound_cl_arg compound_cl_args[] = {
|
|||||||
{ /* arg: xmpp */
|
{ /* arg: xmpp */
|
||||||
.regex = "(.+):(\\w+)",
|
.regex = "(.+):(\\w+)",
|
||||||
.arg_cl = & sslhcfg_xmpp,
|
.arg_cl = & sslhcfg_xmpp,
|
||||||
.base_entry = & table_sslhcfg [27],
|
.base_entry = & table_sslhcfg [29],
|
||||||
.targets = sslhcfg_xmpp_targets,
|
.targets = sslhcfg_xmpp_targets,
|
||||||
|
|
||||||
|
|
||||||
@@ -1596,7 +1630,7 @@ static struct compound_cl_arg compound_cl_args[] = {
|
|||||||
{ /* arg: http */
|
{ /* arg: http */
|
||||||
.regex = "(.+):(\\w+)",
|
.regex = "(.+):(\\w+)",
|
||||||
.arg_cl = & sslhcfg_http,
|
.arg_cl = & sslhcfg_http,
|
||||||
.base_entry = & table_sslhcfg [27],
|
.base_entry = & table_sslhcfg [29],
|
||||||
.targets = sslhcfg_http_targets,
|
.targets = sslhcfg_http_targets,
|
||||||
|
|
||||||
|
|
||||||
@@ -1608,7 +1642,7 @@ static struct compound_cl_arg compound_cl_args[] = {
|
|||||||
{ /* arg: adb */
|
{ /* arg: adb */
|
||||||
.regex = "(.+):(\\w+)",
|
.regex = "(.+):(\\w+)",
|
||||||
.arg_cl = & sslhcfg_adb,
|
.arg_cl = & sslhcfg_adb,
|
||||||
.base_entry = & table_sslhcfg [27],
|
.base_entry = & table_sslhcfg [29],
|
||||||
.targets = sslhcfg_adb_targets,
|
.targets = sslhcfg_adb_targets,
|
||||||
|
|
||||||
|
|
||||||
@@ -1620,7 +1654,7 @@ static struct compound_cl_arg compound_cl_args[] = {
|
|||||||
{ /* arg: socks5 */
|
{ /* arg: socks5 */
|
||||||
.regex = "(.+):(\\w+)",
|
.regex = "(.+):(\\w+)",
|
||||||
.arg_cl = & sslhcfg_socks5,
|
.arg_cl = & sslhcfg_socks5,
|
||||||
.base_entry = & table_sslhcfg [27],
|
.base_entry = & table_sslhcfg [29],
|
||||||
.targets = sslhcfg_socks5_targets,
|
.targets = sslhcfg_socks5_targets,
|
||||||
|
|
||||||
|
|
||||||
@@ -1632,7 +1666,7 @@ static struct compound_cl_arg compound_cl_args[] = {
|
|||||||
{ /* arg: syslog */
|
{ /* arg: syslog */
|
||||||
.regex = "(.+):(\\w+)",
|
.regex = "(.+):(\\w+)",
|
||||||
.arg_cl = & sslhcfg_syslog,
|
.arg_cl = & sslhcfg_syslog,
|
||||||
.base_entry = & table_sslhcfg [27],
|
.base_entry = & table_sslhcfg [29],
|
||||||
.targets = sslhcfg_syslog_targets,
|
.targets = sslhcfg_syslog_targets,
|
||||||
|
|
||||||
|
|
||||||
@@ -1644,7 +1678,7 @@ static struct compound_cl_arg compound_cl_args[] = {
|
|||||||
{ /* arg: msrdp */
|
{ /* arg: msrdp */
|
||||||
.regex = "(.+):(\\w+)",
|
.regex = "(.+):(\\w+)",
|
||||||
.arg_cl = & sslhcfg_msrdp,
|
.arg_cl = & sslhcfg_msrdp,
|
||||||
.base_entry = & table_sslhcfg [27],
|
.base_entry = & table_sslhcfg [29],
|
||||||
.targets = sslhcfg_msrdp_targets,
|
.targets = sslhcfg_msrdp_targets,
|
||||||
|
|
||||||
|
|
||||||
@@ -1656,7 +1690,7 @@ static struct compound_cl_arg compound_cl_args[] = {
|
|||||||
{ /* arg: anyprot */
|
{ /* arg: anyprot */
|
||||||
.regex = "(.+):(\\w+)",
|
.regex = "(.+):(\\w+)",
|
||||||
.arg_cl = & sslhcfg_anyprot,
|
.arg_cl = & sslhcfg_anyprot,
|
||||||
.base_entry = & table_sslhcfg [27],
|
.base_entry = & table_sslhcfg [29],
|
||||||
.targets = sslhcfg_anyprot_targets,
|
.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", "<path>", 0, 1, "Root to change to after set-up"),
|
sslhcfg_chroot = arg_strn("C", "chroot", "<path>", 0, 1, "Root to change to after set-up"),
|
||||||
sslhcfg_syslog_facility = arg_strn(NULL, "syslog-facility", "<str>", 0, 1, "Facility to syslog to"),
|
sslhcfg_syslog_facility = arg_strn(NULL, "syslog-facility", "<str>", 0, 1, "Facility to syslog to"),
|
||||||
sslhcfg_logfile = arg_strn(NULL, "logfile", "<str>", 0, 1, "Log messages to a file"),
|
sslhcfg_logfile = arg_strn(NULL, "logfile", "<str>", 0, 1, "Log messages to a file"),
|
||||||
|
sslhcfg_ech_conf_sink = arg_strn(NULL, "ech-conf-sink", "<str>", 0, 1, "File where to write ECH configurations"),
|
||||||
|
sslhcfg_ech_public_name = arg_strn(NULL, "ech-public-name", "<str>", 0, 1, "Public name for ECH client-facing proxy (usually, sslh's address)"),
|
||||||
sslhcfg_on_timeout = arg_strn(NULL, "on-timeout", "<str>", 0, 1, "Target to connect to when timing out"),
|
sslhcfg_on_timeout = arg_strn(NULL, "on-timeout", "<str>", 0, 1, "Target to connect to when timing out"),
|
||||||
sslhcfg_prefix = arg_strn(NULL, "prefix", "<str>", 0, 1, "Reserved for testing"),
|
sslhcfg_prefix = arg_strn(NULL, "prefix", "<str>", 0, 1, "Reserved for testing"),
|
||||||
sslhcfg_listen = arg_strn("p", "listen", "<host:port>", 0, 10, "Listen on host:port"),
|
sslhcfg_listen = arg_strn("p", "listen", "<host:port>", 0, 10, "Listen on host:port"),
|
||||||
@@ -2621,6 +2657,16 @@ void sslhcfg_fprint(
|
|||||||
fprintf(out, " <unset>");
|
fprintf(out, " <unset>");
|
||||||
fprintf(out, "\n");
|
fprintf(out, "\n");
|
||||||
indent(out, depth);
|
indent(out, depth);
|
||||||
|
fprintf(out, "ech_conf_sink: %s", sslhcfg->ech_conf_sink);
|
||||||
|
if (! sslhcfg->ech_conf_sink_is_present)
|
||||||
|
fprintf(out, " <unset>");
|
||||||
|
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, " <unset>");
|
||||||
|
fprintf(out, "\n");
|
||||||
|
indent(out, depth);
|
||||||
fprintf(out, "on_timeout: %s", sslhcfg->on_timeout);
|
fprintf(out, "on_timeout: %s", sslhcfg->on_timeout);
|
||||||
fprintf(out, "\n");
|
fprintf(out, "\n");
|
||||||
indent(out, depth);
|
indent(out, depth);
|
||||||
|
|||||||
+6
-1
@@ -1,5 +1,5 @@
|
|||||||
/* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README)
|
/* 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
|
# conf2struct: generate libconf parsers that read to structs
|
||||||
# Copyright (C) 2018-2024 Yves Rutschle
|
# Copyright (C) 2018-2024 Yves Rutschle
|
||||||
@@ -38,6 +38,7 @@
|
|||||||
#include "probe.h"
|
#include "probe.h"
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
#include <wolfssl/ssl.h>
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
|
|
||||||
struct sslhcfg_listen_item {
|
struct sslhcfg_listen_item {
|
||||||
@@ -114,6 +115,10 @@ struct sslhcfg_item {
|
|||||||
char* syslog_facility;
|
char* syslog_facility;
|
||||||
int logfile_is_present;
|
int logfile_is_present;
|
||||||
char* logfile;
|
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* on_timeout;
|
||||||
char* prefix;
|
char* prefix;
|
||||||
size_t listen_len;
|
size_t listen_len;
|
||||||
|
|||||||
+1
-1
@@ -347,7 +347,7 @@ int main(int argc, char *argv[], char* envp[])
|
|||||||
/* Open log file for writing */
|
/* Open log file for writing */
|
||||||
setup_logfile();
|
setup_logfile();
|
||||||
|
|
||||||
ech_init();
|
ech_init(listen_sockets, num_addr_listen);
|
||||||
|
|
||||||
if (cfg.user || cfg.chroot)
|
if (cfg.user || cfg.chroot)
|
||||||
drop_privileges(cfg.user, cfg.chroot);
|
drop_privileges(cfg.user, cfg.chroot);
|
||||||
|
|||||||
+2
-4
@@ -86,6 +86,8 @@ config: {
|
|||||||
description: "Facility to syslog to"; },
|
description: "Facility to syslog to"; },
|
||||||
{ name: "logfile"; type: "string"; optional: true;
|
{ name: "logfile"; type: "string"; optional: true;
|
||||||
description: "Log messages to a file" },
|
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";
|
{ name: "on-timeout"; type: "string"; default: "ssh";
|
||||||
description: "Target to connect to when timing out"; },
|
description: "Target to connect to when timing out"; },
|
||||||
@@ -131,10 +133,6 @@ config: {
|
|||||||
type: "array",
|
type: "array",
|
||||||
element_type: "string"
|
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",
|
{ name: "alpn_protocols",
|
||||||
type: "array",
|
type: "array",
|
||||||
element_type: "string"
|
element_type: "string"
|
||||||
|
|||||||
@@ -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 parse_alpn_extension(const struct TLSProtocol *, const char *, size_t);
|
||||||
static int has_match(const char**, size_t, 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
|
/* Parse a TLS packet for the Server Name Indication and ALPN extension in the client
|
||||||
* hello handshake, returning a status code
|
* 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)
|
if (data_len < TLS_HEADER_LEN)
|
||||||
return TLS_ELENGTH;
|
return TLS_ELENGTH;
|
||||||
|
|
||||||
|
/* If if it is an ECH, decrypt and match */
|
||||||
|
ech_match_sni(data, data_len);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
tls_content_type = data[0];
|
tls_content_type = data[0];
|
||||||
if (tls_content_type != TLS_HANDSHAKE_CONTENT_TYPE) {
|
if (tls_content_type != TLS_HANDSHAKE_CONTENT_TYPE) {
|
||||||
print_message(msg_probe_info, "Request did not begin with TLS handshake.\n");
|
print_message(msg_probe_info, "Request did not begin with TLS handshake.\n");
|
||||||
|
|||||||
@@ -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_ENOEXT -4 /* No ALPN or SNI extension found */
|
||||||
#define TLS_EPROTOCOL -5 /* Protocol error */
|
#define TLS_EPROTOCOL -5 /* Protocol error */
|
||||||
|
|
||||||
|
void ech_match_sni(const char* buffer_in, size_t buffer_len);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -33,34 +33,134 @@
|
|||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "sslh-conf.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;
|
WOLFSSL* ssl = NULL;
|
||||||
|
|
||||||
|
printf("wolfSSL_new(%p)\n", ctx);
|
||||||
ssl = wolfSSL_new(ctx);
|
ssl = wolfSSL_new(ctx);
|
||||||
if (ssl == NULL) {
|
if (ssl == NULL) {
|
||||||
fprintf(stderr, "Failed to create WOLFSSL object\n");
|
fprintf(stderr, "Failed to create WOLFSSL object\n");
|
||||||
close(client_fd);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable certificate verification on this connection
|
// Disable certificate verification on this connection
|
||||||
wolfSSL_set_verify(ssl, WOLFSSL_VERIFY_NONE, NULL);
|
wolfSSL_set_verify(ssl, WOLFSSL_VERIFY_NONE, NULL);
|
||||||
|
|
||||||
|
byte client_hello_inner[1024];
|
||||||
|
word32 ch_len = sizeof(client_hello_inner);;
|
||||||
|
|
||||||
char* sni_data = NULL;
|
int res = wolfSSL_GetEchClientHelloInner(
|
||||||
word16 sni_len = wolfSSL_SNI_GetRequest(ssl, WOLFSSL_SNI_HOST_NAME, (void**)&sni_data);
|
ssl,
|
||||||
#endif
|
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));
|
print_message(msg_config_error, "Unable to open %s:%d:%s\n", cfg.ech_conf_sink, errno, strerror(errno));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
printf("erased and opend %s\n", cfg.ech_conf_sink);
|
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;
|
int ret;
|
||||||
|
|
||||||
|
printf("ech_genkey for ctx %p\n", ctx);
|
||||||
|
|
||||||
print_message(msg_config, "Generating ECH config for outer SNI: %s\n", sni);
|
print_message(msg_config, "Generating ECH config for outer SNI: %s\n", sni);
|
||||||
ret = wolfSSL_CTX_GenerateEchConfig(ctx, sni, 0, 0, 0);
|
ret = wolfSSL_CTX_GenerateEchConfig(ctx, sni, 0, 0, 0);
|
||||||
if (ret != WOLFSSL_SUCCESS) {
|
if (ret != WOLFSSL_SUCCESS) {
|
||||||
@@ -119,7 +219,7 @@ static void ech_genkey(WOLFSSL_CTX* ctx, const char* sni)
|
|||||||
}
|
}
|
||||||
|
|
||||||
char out[512];
|
char out[512];
|
||||||
int outLen = sizeof(out);
|
word32 outLen = sizeof(out);
|
||||||
if (Base64_Encode_NoNl(echConfig, echConfigLen, (byte*)out, &outLen) != 0) {
|
if (Base64_Encode_NoNl(echConfig, echConfigLen, (byte*)out, &outLen) != 0) {
|
||||||
print_message(msg_system_error, "Failed to encode to base64: %d\n", ret);
|
print_message(msg_system_error, "Failed to encode to base64: %d\n", ret);
|
||||||
exit(1);
|
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 */
|
* (Later, this will load the configurations from some place else */
|
||||||
static void ech_create_configs(WOLFSSL_CTX** ctx)
|
static void ech_create_configs(WOLFSSL_CTX** ctx)
|
||||||
{
|
{
|
||||||
int i, j;
|
int ret;
|
||||||
|
|
||||||
reset_conf();
|
reset_conf();
|
||||||
|
|
||||||
for (i = 0; i < cfg.listen_len; i++) {
|
*ctx = wolfSSL_CTX_new(wolfTLSv1_3_server_method());
|
||||||
struct sslhcfg_protocols_item* listen = &cfg.listen[i];
|
if (!*ctx) {
|
||||||
printf("1\n");
|
print_message(msg_system_error, "Failed to create WOLFSSL_CTX\n");
|
||||||
*ctx = malloc(cfg.listen_len * sizeof(*ctx));
|
exit(1);
|
||||||
/* 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");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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,
|
/* Initialises the ECH subsystem: create wolfSSL context, create ECH configs,
|
||||||
* etc */
|
* etc */
|
||||||
|
|
||||||
void ech_init()
|
void ech_init()
|
||||||
{
|
{
|
||||||
WOLFSSL_CTX* ctx = NULL;
|
|
||||||
|
|
||||||
wolfSSL_Init();
|
wolfSSL_Init();
|
||||||
|
|
||||||
ech_create_configs(&ctx);
|
ech_create_configs(&wolfssl_ctx);
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user