mirror of
https://github.com/yrutschle/sslh.git
synced 2026-10-22 18:12:10 +03:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b12220e640 | ||
|
|
204305a88f | ||
|
|
0f96ed8adb | ||
|
|
43e75a0a8c | ||
|
|
ad1f5d68e9 | ||
|
|
ff8206f7c8 | ||
|
|
8298daf686 | ||
|
|
168477ea34 | ||
|
|
b5d4d4662b | ||
|
|
6e6d94752a | ||
|
|
b2bcfc26b2 |
No files matched your search
@@ -10,6 +10,7 @@ sslh-ev
|
||||
systemd-sslh-generator
|
||||
sslh.8.gz
|
||||
tags
|
||||
version.h
|
||||
/config.status
|
||||
/config.log
|
||||
/config.h
|
||||
|
||||
@@ -1,3 +1,21 @@
|
||||
v2.2.4:
|
||||
Fix CVE-2025-46806 for "Misaligned Memory Accesses in `is_openvpn_protocol()`"
|
||||
|
||||
Fix CVE-2025-46807 for "File Descriptor Exhaustion in sslh-select and sslh-ev"
|
||||
|
||||
Fix potential parsing of undefined data in syslog
|
||||
probe (no CVE assigned)
|
||||
|
||||
Thanks to Matthias Gerstner of the SUSE security
|
||||
team for the security review that found these
|
||||
defects!
|
||||
|
||||
v2.2.3:
|
||||
Reverse older commit: version.h cannot be included
|
||||
without breaking the build (everything recompiles
|
||||
every time) and the release archive creation (which
|
||||
relies on git tags).
|
||||
|
||||
v2.2.2:
|
||||
Fix potential vulnerability similar to CVE-2020-28935
|
||||
|
||||
|
||||
+2
-4
@@ -22,7 +22,7 @@ MAN=sslh.8.gz # man page name
|
||||
# itself
|
||||
|
||||
ifneq ($(strip $(ENABLE_SANITIZER)),)
|
||||
CFLAGS_SAN=-fsanitize=address -fsanitize=leak -fsanitize=undefined
|
||||
CFLAGS_SAN=-fsanitize=address -fsanitize=leak -fsanitize=undefined -fsanitize=alignment
|
||||
endif
|
||||
|
||||
ifneq ($(strip $(COV_TEST)),)
|
||||
@@ -70,9 +70,8 @@ all: sslh-fork sslh-select $(MAN) echosrv $(CONDITIONAL_TARGETS)
|
||||
$(OBJS_A): $(OBJS)
|
||||
$(AR) rcs $(OBJS_A) $(OBJS)
|
||||
|
||||
version.h: .FORCE
|
||||
version.h:
|
||||
./genver.sh >version.h
|
||||
.FORCE:
|
||||
|
||||
$(OBJS) $(FORK_OBJS) $(SELECT_OBJS) $(EV_OBJS): argtable3.h collection.h common.h gap.h hash.h log.h probe.h processes.h sslh-conf.h tcp-listener.h tcp-probe.h tls.h udp-listener.h version.h
|
||||
|
||||
@@ -145,7 +144,6 @@ distclean: clean
|
||||
|
||||
clean:
|
||||
rm -f sslh-fork sslh-select $(CONDITIONAL_TARGETS) echosrv version.h $(MAN) systemd-sslh-generator *.o *.gcov *.gcno *.gcda *.png *.html *.css *.info
|
||||
echo "// this is a placeholder for version.h, to make code-checking editors happy" > version.h
|
||||
|
||||
tags:
|
||||
ctags --globals -T *.[ch]
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#define PCRE2_CODE_UNIT_WIDTH 8
|
||||
#include <pcre2.h>
|
||||
#endif
|
||||
#include <regex.h>
|
||||
#include <ctype.h>
|
||||
#include "probe.h"
|
||||
#include "log.h"
|
||||
@@ -146,6 +147,7 @@ static int is_ssh_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_
|
||||
#define OVPN_OPCODE_MASK 0xF8
|
||||
#define OVPN_CONTROL_HARD_RESET_CLIENT_V1 (0x01 << 3)
|
||||
#define OVPN_CONTROL_HARD_RESET_CLIENT_V2 (0x07 << 3)
|
||||
#define OVPN_CONTROL_HARD_RESET_CLIENT_V3 (0x0A << 3)
|
||||
#define OVPN_HMAC_128 16
|
||||
#define OVPN_HMAC_160 20
|
||||
#define OVPN_HARD_RESET_PACKET_ID_OFFSET(hmac_size) (9 + hmac_size)
|
||||
@@ -164,8 +166,12 @@ static int is_openvpn_protocol (const char*p,ssize_t len, struct sslhcfg_protoco
|
||||
if (len < 1)
|
||||
return PROBE_NEXT;
|
||||
|
||||
printf("opcode: %d\n", (p[0] & OVPN_OPCODE_MASK) >> 3);
|
||||
|
||||
if ((p[0] & OVPN_OPCODE_MASK) != OVPN_CONTROL_HARD_RESET_CLIENT_V1 &&
|
||||
(p[0] & OVPN_OPCODE_MASK) != OVPN_CONTROL_HARD_RESET_CLIENT_V2)
|
||||
(p[0] & OVPN_OPCODE_MASK) != OVPN_CONTROL_HARD_RESET_CLIENT_V2 &&
|
||||
(p[0] & OVPN_OPCODE_MASK) != OVPN_CONTROL_HARD_RESET_CLIENT_V3
|
||||
)
|
||||
return PROBE_NEXT;
|
||||
|
||||
/* The detection pattern above may not be reliable enough.
|
||||
@@ -176,12 +182,18 @@ static int is_openvpn_protocol (const char*p,ssize_t len, struct sslhcfg_protoco
|
||||
if (len <= OVPN_HARD_RESET_PACKET_ID_OFFSET(OVPN_HMAC_128) + sizeof(uint32_t))
|
||||
return PROBE_NEXT;
|
||||
|
||||
if (ntohl(*(uint32_t*)(p + OVPN_HARD_RESET_PACKET_ID_OFFSET(OVPN_HMAC_128))) <= 5u)
|
||||
uint32_t i;
|
||||
/* OVPN_HMAC_128 is unaligned, which requires special care e.g. on ARM */
|
||||
memcpy(&i, (p + OVPN_HARD_RESET_PACKET_ID_OFFSET(OVPN_HMAC_128)), sizeof(i));
|
||||
i = ntohl(i);
|
||||
if (i <= 5u)
|
||||
return PROBE_MATCH;
|
||||
|
||||
if (len <= OVPN_HARD_RESET_PACKET_ID_OFFSET(OVPN_HMAC_160) + sizeof(uint32_t))
|
||||
return PROBE_NEXT;
|
||||
|
||||
memcpy(&i, (p + OVPN_HARD_RESET_PACKET_ID_OFFSET(OVPN_HMAC_160)), sizeof(i));
|
||||
i = ntohl(i);
|
||||
if (ntohl(*(uint32_t*)(p + OVPN_HARD_RESET_PACKET_ID_OFFSET(OVPN_HMAC_160))) <= 5u)
|
||||
return PROBE_MATCH;
|
||||
|
||||
@@ -362,17 +374,38 @@ static int is_socks5_protocol(const char *p_in, ssize_t len, struct sslhcfg_prot
|
||||
return PROBE_MATCH;
|
||||
}
|
||||
|
||||
/* ******************
|
||||
* is_syslog_protocol
|
||||
* */
|
||||
static regex_t syslog_preg;
|
||||
static int configured_syslog_regex = 0;
|
||||
|
||||
static void config_syslog_regex(void)
|
||||
{
|
||||
/* two patterns for syslog messages:
|
||||
* <12> My message
|
||||
* 15 <12> My message
|
||||
* 12 is 'priority', 1 to 3 digits (RFC4234)
|
||||
* 15 is 'message length', a TCP-only option (RFC6587)
|
||||
*/
|
||||
int res = regcomp(&syslog_preg, "^([0-9]{1,3} )?<[0-9]{1,3}>", REG_EXTENDED);
|
||||
if (res) {
|
||||
print_message(msg_system_error, "regcomp");
|
||||
exit(1);
|
||||
}
|
||||
configured_syslog_regex = 1;
|
||||
}
|
||||
|
||||
static int is_syslog_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item* proto)
|
||||
{
|
||||
int res, i, j;
|
||||
char buf[len+1];
|
||||
|
||||
res = sscanf(p, "<%d>", &i);
|
||||
if (res == 1) return 1;
|
||||
if (!configured_syslog_regex) config_syslog_regex();
|
||||
|
||||
res = sscanf(p, "%d <%d>", &i, &j);
|
||||
if (res == 2) return 1;
|
||||
strncpy(buf, p, len);
|
||||
buf[len] = 0;
|
||||
|
||||
return 0;
|
||||
return (regexec(&syslog_preg, buf, (size_t)0, NULL, 0) == 0);
|
||||
}
|
||||
|
||||
static int is_teamspeak_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item* proto)
|
||||
|
||||
+7
-5
@@ -156,13 +156,15 @@ void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen)
|
||||
/* Check main socket for new connections */
|
||||
for (i = 0; i < num_addr_listen; i++) {
|
||||
if (FD_ISSET(listen_sockets[i].socketfd, &readfds)) {
|
||||
struct connection* new_cnx = cnx_accept_process(&fd_info, &listen_sockets[i]);
|
||||
|
||||
if (fd_out_of_range(new_cnx->q[0].fd))
|
||||
tidy_connection(new_cnx, &fd_info);
|
||||
|
||||
/* don't also process it as a read socket */
|
||||
FD_CLR(listen_sockets[i].socketfd, &readfds);
|
||||
|
||||
struct connection* new_cnx;
|
||||
while ((new_cnx = cnx_accept_process(&fd_info, &listen_sockets[i]))) {
|
||||
if (fd_out_of_range(new_cnx->q[0].fd))
|
||||
tidy_connection(new_cnx, &fd_info);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
use strict;
|
||||
use IO::Socket::INET6;
|
||||
use Test::More qw/no_plan/;
|
||||
use Conf::Libconfig;
|
||||
use Conf::Libconfig 1.0.3;
|
||||
|
||||
my $conf = new Conf::Libconfig;
|
||||
$conf->read_file("test.cfg");
|
||||
@@ -17,7 +17,7 @@ $conf->read_file("test.cfg");
|
||||
|
||||
my $no_listen = 8083; # Port on which no-one listens
|
||||
my $pidfile = $conf->lookup_value("pidfile");
|
||||
my $sslh_port = $conf->fetch_array("listen")->[0]->{port};
|
||||
my $sslh_port = $conf->value("listen")->[0]->{port};
|
||||
my $user = (getpwuid $<)[0]; # Run under current username
|
||||
|
||||
# Which tests do we run
|
||||
@@ -84,8 +84,9 @@ sub make_sni_alpn_name {
|
||||
sub test_probe {
|
||||
my (%opts) = @_;
|
||||
|
||||
print "test_probe [$opts{expected}] $sslh_port\n";
|
||||
my $cnx = new IO::Socket::INET(PeerHost => "localhost:$sslh_port");
|
||||
warn "$!\n" unless $cnx;
|
||||
warn "t: $!\n" unless $cnx;
|
||||
return unless $cnx;
|
||||
|
||||
my $pattern = $opts{data};
|
||||
@@ -119,7 +120,7 @@ sub test_probe {
|
||||
sub test_probes {
|
||||
my (%in_opts) = @_;
|
||||
|
||||
my @probes = @{$conf->fetch_array("protocols")};
|
||||
my @probes = @{$conf->value("protocols")};
|
||||
foreach my $p (@probes) {
|
||||
my %protocols = (
|
||||
'ssh' => { data => "SSH-2.0 tester" },
|
||||
@@ -194,7 +195,7 @@ sub test_probes {
|
||||
|
||||
|
||||
# Start an echoserver for each service
|
||||
foreach my $s (@{$conf->fetch_array("protocols")}) {
|
||||
foreach my $s (@{$conf->value("protocols")}) {
|
||||
my $prefix = $s->{name};
|
||||
|
||||
$prefix =~ s/^ssl/tls/; # To remove in 1.21
|
||||
@@ -217,7 +218,7 @@ for my $binary (@binaries) {
|
||||
my ($sslh_pid, $valgrind);
|
||||
if (!($sslh_pid = fork)) {
|
||||
my $user = (getpwuid $<)[0]; # Run under current username
|
||||
my $cmd = "./$binary -v 4 -f -u $user -F test.cfg";
|
||||
my $cmd = "./$binary -u $user -F test.cfg";
|
||||
#$valgrind = 1;
|
||||
#$cmd = "valgrind --leak-check=full $cmd";
|
||||
verbose_exec $cmd;
|
||||
@@ -339,11 +340,11 @@ if ($RB_CNX_NOSERVER) {
|
||||
}
|
||||
|
||||
|
||||
my $ssh_conf = (grep { $_->{name} eq "ssh" } @{$conf->fetch_array("protocols")})[0];
|
||||
my $ssh_conf = (grep { $_->{name} eq "ssh" } @{$conf->value("protocols")})[0];
|
||||
my $ssh_address = $ssh_conf->{host} . ":" . $ssh_conf->{port};
|
||||
|
||||
# Use the last TLS echoserv (no SNI/ALPN)
|
||||
my $ssl_conf = (grep { $_->{name} eq "tls" } @{$conf->fetch_array("protocols")})[-1];
|
||||
my $ssl_conf = (grep { $_->{name} eq "tls" } @{$conf->value ("protocols")})[-1];
|
||||
my $ssl_address = $ssl_conf->{host} . ":" . $ssl_conf->{port};
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user