misc hardening: connection drop, NULL deref, OOB writes, dead option

- shovel(): fd2fd() returns FD_NODATA (-1) when a spurious wakeup
  meets an empty socket (EAGAIN), but the switch treated -1 like
  FD_CNXCLOSED and tore down a healthy connection. Only FD_CNXCLOSED
  closes now.

- decrease_forked_connection(): hash_find() can return NULL (stale
  SIGCHLD after a fork-limit rejection); dereferencing it crashed.
  Guard and log instead.

- hexdump(): sprintf() into a 76-byte stack buffer with a "%06x"
  offset that needs 12 chars once the dump passes 16 MB. Enlarge the
  buffer and bound all writes with snprintf().

- printsettings(): sprintf() of config-supplied host strings into a
  fixed buffer; use snprintf().

- echosrv: recvfrom() into data+prefix_len with size
  sizeof(data)-prefix_len lets the terminating NUL write land one
  byte past the stack buffer (verified with ASan at udp_echo, and a
  >64KB prefix overflows the memcpy). Bound the prefix, reserve one
  byte for the terminator, and skip the echo on recvfrom() error
  instead of writing data[prefix_len-1].

- msg_int_error used verbose_system_error, leaving the documented
  --verbose-int-error option dead. Point it at its own setting.

- Remove "echoѕrv-conf.h" (with Cyrillic ѕ, U+0455): a stale 2020
  sslh-conf.h copy committed under a filename that is visually
  identical to echosrv-conf.h but referenced by nothing.
This commit is contained in:
Stefan Meinecke authored and Yves Rutschle committed 2026-10-18 22:53:40 +02:00
1 parent b9d9f9112f
commit bb89714216
7 files changed
+23 -120

No files matched your search

+11 -2
View File
@@ -68,9 +68,13 @@ void start_echo(int fd)
int first = 1;
prefix_len = strlen(cfg.prefix);
if (prefix_len >= sizeof(buffer)) {
fprintf(stderr, "prefix too long\n");
return;
}
memset(buffer, 0, sizeof(buffer));
strcpy(buffer, cfg.prefix);
memcpy(buffer, cfg.prefix, prefix_len);
while (1) {
ret = read(fd, buffer + prefix_len, sizeof(buffer) - prefix_len);
@@ -147,19 +151,24 @@ void udp_echo(struct listen_endpoint* listen_socket)
memset(data, 0, sizeof(data));
size_t prefix_len = strlen(cfg.prefix);
if (prefix_len >= sizeof(data) - 1) {
fprintf(stderr, "prefix too long\n");
return;
}
memcpy(data, cfg.prefix, prefix_len);
while (1) {
addrlen = sizeof(src_addr);
ssize_t len = recvfrom(listen_socket->socketfd,
data + prefix_len,
sizeof(data) - prefix_len,
sizeof(data) - prefix_len - 1,
0,
&src_addr,
&addrlen);
if (len < 0) {
perror("recvfrom");
continue;
}
*(data + prefix_len + len) = 0;
fprintf(stderr, "%zd %s\n", len, data + prefix_len);
-109
View File
@@ -1,109 +0,0 @@
/* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README)
* on Sat Nov 7 09:19:26 2020.
# conf2struct: generate libconf parsers that read to structs
# Copyright (C) 2018-2019 Yves Rutschle
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef C2S_SSLHCFG_H
#define C2S_SSLHCFG_H
#ifdef LIBCONFIG
# include <libconfig.h>
#endif
#include "probe.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
struct sslhcfg_listen_item {
char* host;
char* port;
int keepalive;
};
struct sslhcfg_protocols_item {
char* name;
char* host;
char* port;
int service_is_present;
char* service;
int fork;
int tfo_ok;
int log_level;
int keepalive;
size_t sni_hostnames_len;
char** sni_hostnames;
size_t alpn_protocols_len;
char** alpn_protocols;
size_t regex_patterns_len;
char** regex_patterns;
int minlength_is_present;
int minlength;
T_PROBE* probe;
struct addrinfo* saddr;
void* data;
};
struct sslhcfg_item {
char* prefix;
int verbose;
int foreground;
int inetd;
int numeric;
int transparent;
int timeout;
int user_is_present;
char* user;
int pidfile_is_present;
char* pidfile;
int chroot_is_present;
char* chroot;
char* syslog_facility;
char* on_timeout;
size_t listen_len;
struct sslhcfg_listen_item* listen;
size_t protocols_len;
struct sslhcfg_protocols_item* protocols;
};
int sslhcfg_parse_file(
const char* filename,
struct sslhcfg_item* sslhcfg,
const char** errmsg);
void sslhcfg_fprint(
FILE* out,
struct sslhcfg_item *sslhcfg,
int depth);
int sslhcfg_cl_parse(
int argc,
char* argv[],
struct sslhcfg_item *sslhcfg);
#endif
+1 -1
View File
@@ -48,7 +48,7 @@ msg_info msg_fd = {
/* Internal errors: inconsistent states, impossible values, things that should never happen, and are therefore the sign of memory corruption: hence the LOG_CRIT */
msg_info msg_int_error = {
LOG_CRIT,
&cfg.verbose_system_error
&cfg.verbose_int_error
};
/* System errors: when the system around us fails us: memory allocation, fork, ... */
+4 -4
View File
@@ -88,20 +88,20 @@ struct sslhcfg_protocols_item* timeout_protocol(void)
void hexdump(msg_info msg_info, const char *mem, unsigned int len)
{
unsigned int i, j;
char str[10 + HEXDUMP_COLS * 4 + 2];
char str[12 + HEXDUMP_COLS * 4 + 2]; /* offset <= 12, hex+ascii 64, '\n'+'\0' */
int c = 0; /* index in str */
for(i = 0; i < len + ((len % HEXDUMP_COLS) ? (HEXDUMP_COLS - len % HEXDUMP_COLS) : 0); i++)
{
/* print offset */
if(i % HEXDUMP_COLS == 0)
c += sprintf(&str[c], "0x%06x: ", i);
c += snprintf(&str[c], sizeof(str) - c, "0x%06x: ", i);
/* print hex data */
if(i < len)
c += sprintf(&str[c], "%02x ", 0xFF & mem[i]);
c += snprintf(&str[c], sizeof(str) - c, "%02x ", 0xFF & mem[i]);
else /* end of block, just aligning for ASCII dump */
c+= sprintf(&str[c], " ");
c += snprintf(&str[c], sizeof(str) - c, " ");
/* print ASCII dump */
if(i % HEXDUMP_COLS == (HEXDUMP_COLS - 1)) {
+4
View File
@@ -142,6 +142,10 @@ static int pid_cmp(hash_item item1, hash_item item2)
void decrease_forked_connection(struct loop_info* loop, pid_t pid) {
struct pid2proto p2p = { .pid = pid };
struct pid2proto* found = hash_find(loop->pid2proto, &p2p);
if (!found) {
print_message(msg_int_error, "SIGCHLD for unknown pid %d\n", pid);
return;
}
dec_proto_connections(found->proto);
dec_listen_connections(found->endpoint);
+3 -3
View File
@@ -74,16 +74,16 @@ static void printsettings(void)
for (i = 0; i < cfg.protocols_len; i++ ) {
p = &cfg.protocols[i];
if (p->is_unix) {
sprintf(buf, "unix socket: %s", p->host);
snprintf(buf, sizeof(buf), "unix socket: %s", p->host);
} else {
if (p->resolve_on_forward) {
strcpy(buf, "resolve on forward: ");
size_t len = strlen(buf);
sprintf(buf+len, "%s:%s", p->host, p->port);
snprintf(buf+len, sizeof(buf)-len, "%s:%s", p->host, p->port);
} else {
sprintaddr(buf, sizeof(buf), p->saddr);
size_t len = strlen(buf);
sprintf(buf+len, " family %d %d",
snprintf(buf+len, sizeof(buf)-len, " family %d %d",
p->saddr->ai_family,
p->saddr->ai_addr->sa_family);
}
-1
View File
@@ -55,7 +55,6 @@ static void shovel(struct connection *cnx, int active_fd, struct loop_info* fd_i
print_message(msg_fd, "activity on fd%d\n", read_q->fd);
switch(fd2fd(write_q, read_q)) {
case -1:
case FD_CNXCLOSED:
tidy_connection(cnx, fd_info);
break;