Compare commits

...
32 Commits
Author SHA1 Message Date
Yves Rutschle c43f6daf03 v.2.1.0 2024-03-13 21:14:52 +01:00
Yves Rűtschlé 736b108a75 dont log error when remote client drops connection on Windows (fix #427) 2024-03-13 16:58:09 +01:00
Yves Rutschle 7ca567fcd9 clarify the difficulty of transparent proxying 2024-03-12 22:07:17 +01:00
Yves Rutschle 3117c15fbd when sslh-fork accept() fails with some errors, retry accept instead of dying 2024-01-11 21:32:39 +01:00
Yves Rutschle e428fc505c prevent children from continuing the parent's work in case of failure 2024-01-11 21:24:19 +01:00
Yves Rutschle 4dfb4d300a add default LANDLOCK_ACCESS_FS_REFER define for Ubuntu (fix #420) 2024-01-06 11:56:57 +01:00
Yves Rutschle 39184b5622 enable landlock access to files to allow forking and name resolution 2024-01-05 13:21:55 +01:00
Yves Rutschle 940461de18 check name resolution error to avoid segfault 2024-01-05 11:15:53 +01:00
Yves Rutschle 6f949419d1 fix comment to reflect reality 2024-01-05 10:34:29 +01:00
Yves Rutschle dab5df7409 clarify where the name resolution error comes from 2024-01-05 10:32:41 +01:00
Yves Rutschle 402ca5219b fix warnings on sockaddr storage 2024-01-04 23:22:44 +01:00
Yves Rutschle 046401148d add ./configure stage to docker build 2024-01-04 23:04:26 +01:00
Yves Rutschle 780e536aeb use autoconf to crete build scripts depending on landlock presence (fix #417) 2024-01-04 22:45:10 +01:00
Yves Rutschle ed0ab12a16 reintroduce --ssl as alias to --tls 2024-01-02 21:38:23 +01:00
Yves Rutschle b65f1e8b26 Merged Landlock feature 2023-12-09 14:13:07 +01:00
Konstantin 91b649daa0 Set image tag to use github in docker-compose example (#414)
Change image tag of the docker-compose example from ```sslh:latest``` to ```ghcr.io/yrutschle/sslh:latest```
2023-11-19 20:43:13 +01:00
Latchezar Tzvetkoff 7499c26e9e Cleanup error checking logic in bind_peer() (#412)
Thanks for the cleanup!
2023-11-15 22:02:21 +01:00
Yves Rutschle 90a55b6f9d document Windows build and docker repositories 2023-11-15 21:49:44 +01:00
Yves Rutschle 1f66e2e093 add sslh-ev 2023-11-15 21:46:42 +01:00
Yves Rutschle 92d2326016 Merge pull request #408 from pcrow/master
Fix transparent mode in a multi-stage chain
2023-10-07 21:15:58 +02:00
Preston Crow 81eed9d56a Transparent mode in a multi-stage chain will fail after the first step
because the (ip,port) is already bound.  With this change, the bind is
retried with a different port to at least keep the same IP address, which
for most uses is all that is needed.  I've tested this on my own system
where sslh is used downstream from stunnel, with both in transparent mode.
2023-10-06 17:50:25 -04:00
Yves Rutschle 490a44723b fix install to build sslh-fork (fix #407) 2023-10-05 11:53:09 +02:00
Yves Rutschle 23fb1eba6f remove obsolete intermediate sslh target 2023-10-03 09:34:38 +02:00
Yves Rutschle be66848e2d Merge pull request #406 from ffontaine/master
Makefile: add USE_LIBEV
2023-10-03 09:32:42 +02:00
Fabrice Fontaine 3e93c1d43d Makefile: add USE_LIBEV
Add USE_LIBEV to avoid the following build failure without libev raised
since version 2.0 and
https://github.com/yrutschle/sslh/commit/711c11c820f8be4f1399024bcaa1d7072de33217:

sslh-ev.c:24:10: fatal error: ev.h: Aucun fichier ou dossier de ce type
   24 | #include <ev.h>
      |          ^~~~~~

Signed-off-by: Fabrice Fontaine <fontaine.fabrice@gmail.com>
2023-10-01 18:53:43 +02:00
Yves Rutschle 1b26eb50a5 Changelog for resolve_on_forward fix 2023-09-24 09:25:52 +02:00
Yves Rutschle e0f15a31b7 resolve name at connection time for UDP too 2023-09-12 21:39:51 +02:00
Yves Rutschle c2551c011e update to Conf::Libconfig 1.0.3 API 2023-09-12 21:35:23 +02:00
Yves Rutschle e2c3ed61a8 update to Conf::Libconfig 1.0.3 API 2023-09-12 21:35:10 +02:00
Yves Rutschle 1b0c6d0b8d add resolve_on_forward in tests 2023-09-06 18:01:52 +02:00
Yves Rutschle 0562eb4b07 fix resolve_on_forward use (fix #405) 2023-09-06 15:48:13 +02:00
Yves Rűtschlé 8930ec395e Initial support for the landlock LSM 2023-08-29 17:20:51 +02:00
22 changed files with 4897 additions and 43 deletions

No files matched your search

+2
View File
@@ -3,8 +3,10 @@
*.o
cscope.*
echosrv
libsslh.a
sslh-fork
sslh-select
sslh-ev
sslh.8.gz
tags
version.h
+16
View File
@@ -1,3 +1,19 @@
v2.1.0:
Support for the Landlock LSM. After initial setup,
sslh gives up all local file access rights.
Reintroduced --ssl as an alias to --tls.
Introduce autoconf to adapt to landlock presence.
Close connexion without error message if remote
client forcefully closes connexion, for Windows.
v2.0.1:
Fix resolve_on_forward setting, which would crash
sslh reliably.
v2.0.0:
v2.0:
New sslh-ev: this is functionally equivalent to
sslh-select (mono-process, only forks for specified
+1 -1
View File
@@ -16,7 +16,7 @@ RUN apk add --no-cache \
COPY . /sslh
RUN make sslh-select && strip sslh-select
RUN ./configure && make sslh-select && strip sslh-select
FROM docker.io/${TARGET_ARCH}/alpine:${ALPINE_VERSION}
+12 -6
View File
@@ -3,9 +3,11 @@ VERSION=$(shell ./genver.sh -r)
# Configuration -- you probably need to `make clean` if you
# change any of these
# landlock is checked by `configure` and set in config.h
ENABLE_SANITIZER= # Enable ASAN/LSAN/UBSAN
ENABLE_REGEX=1 # Enable regex probes
USELIBCONFIG=1 # Use libconfig? (necessary to use configuration files)
USELIBEV=1 # Use libev?
USELIBWRAP?= # Use libwrap?
USELIBCAP= # Use libcap?
USESYSTEMD= # Make use of systemd socket activation
@@ -34,7 +36,7 @@ CFLAGS +=-Wall -O2 -DLIBPCRE -g $(CFLAGS_COV) $(CFLAGS_SAN)
LIBS=-lm -lpcre2-8
OBJS=sslh-conf.o common.o log.o sslh-main.o probe.o tls.o argtable3.o collection.o gap.o tcp-probe.o
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
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)
@@ -72,8 +74,11 @@ ifneq ($(strip $(USELIBBSD)),)
CPPFLAGS+=-DLIBBSD
endif
ifneq ($(strip $(USELIBEV)),)
CONDITIONAL_TARGETS+=sslh-ev
endif
all: sslh $(MAN) echosrv $(CONDITIONAL_TARGETS)
all: sslh-fork sslh-select $(MAN) echosrv $(CONDITIONAL_TARGETS)
%.o: %.c %.h version.h
$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@
@@ -84,8 +89,6 @@ $(OBJS_A): $(OBJS)
version.h:
./genver.sh >version.h
sslh: sslh-fork sslh-select sslh-ev
$(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
@@ -114,6 +117,9 @@ echosrv-conf.c echosrv-conf.h: echosrv.cfg
echosrv: version.h echosrv-conf.c echosrv.o echosrv-conf.o argtable3.o
$(CC) $(CFLAGS) $(LDFLAGS) -o echosrv echosrv.o echosrv-conf.o argtable3.o $(LIBS)
landlock.o: config.h
$(MAN): sslh.pod Makefile
pod2man --section=8 --release=$(VERSION) --center=" " sslh.pod | gzip -9 - > $(MAN)
@@ -133,7 +139,7 @@ docker-clean:
yes | docker image prune
# generic install: install binary and man page
install: sslh $(MAN)
install: sslh-fork $(MAN)
mkdir -p $(DESTDIR)/$(BINDIR)
mkdir -p $(DESTDIR)/$(MANDIR)
install -p sslh-fork $(DESTDIR)/$(BINDIR)/sslh
@@ -153,7 +159,7 @@ distclean: clean
rm -f tags sslh-conf.[ch] echosrv-conf.[ch] cscope.*
clean:
rm -f sslh-fork sslh-select sslh-ev echosrv version.h $(MAN) systemd-sslh-generator *.o *.gcov *.gcno *.gcda *.png *.html *.css *.info
rm -f sslh-fork sslh-select $(CONDITIONAL_TARGETS) echosrv version.h $(MAN) systemd-sslh-generator *.o *.gcov *.gcno *.gcda *.png *.html *.css *.info
tags:
ctags --globals -T *.[ch]
+16 -3
View File
@@ -21,8 +21,9 @@ address.
`sslh` has the bells and whistles expected from a mature
daemon: privilege and capabilities dropping, inetd support,
systemd support, transparent proxying, chroot, logging,
IPv4 and IPv6, TCP and UDP, a fork-based and a select-based
model, and more.
IPv4 and IPv6, TCP and UDP, a fork-based, a select-based
model, and yet another based on libev for larger
installations.
Install
=======
@@ -35,6 +36,18 @@ Configuration
Please refer to the [configuration guide](doc/config.md).
Transparent proxying allows the target server to see the
original client IP address, i.e. `sslh` becomes invisible.
This makes it easier to use the server's logs, and potential
IP-based banning ability.
Configuration of transparent proxying is highly dependent on
your network environment and infrastructure setup. There is
no known generic approach, and if you do not find directions
for your exact setup, you will probably need an extensive
knowledge of network management and iptables setup".
It is described in its own [document](tproxy.md).
Docker image
@@ -65,7 +78,7 @@ version: "3"
services:
sslh:
image: sslh:latest
image: ghcr.io/yrutschle/sslh:latest
hostname: sslh
ports:
- 443:443
+26 -6
View File
@@ -110,7 +110,7 @@ int make_listen_tfo(int s)
return setsockopt(s, SOL_SOCKET, TCP_FASTOPEN, (char*)&qlen, sizeof(qlen));
}
/* Starts listening on a single address
/* Starts listening on a single address
* Returns a socket filehandle, or dies with message in case of major error */
int listen_single_addr(struct addrinfo* addr, int keepalive, int udp)
{
@@ -262,7 +262,7 @@ int bind_peer(int fd, int fd_from)
/* if the destination is the same machine, there's no need to do bind */
if (is_same_machine(&from))
return 0;
#ifndef IP_BINDANY /* use IP_TRANSPARENT */
res = setsockopt(fd, IPPROTO_IP, IP_TRANSPARENT, &trans, sizeof(trans));
CHECK_RES_DIE(res, "setsockopt IP_TRANSPARENT");
@@ -278,7 +278,26 @@ int bind_peer(int fd, int fd_from)
}
#endif /* IP_TRANSPARENT / IP_BINDANY */
res = bind(fd, from.ai_addr, from.ai_addrlen);
CHECK_RES_RETURN(res, "bind", res);
if (res == -1) {
if (errno != EADDRINUSE) {
print_message(msg_system_error, "%s:%d:%s:%d:%s\n", __FILE__, __LINE__,
"bind", errno, strerror(errno));
return res;
}
/*
* If there is more than one transparent mode proxy going on, such as
* using sslh as the target of stunnel also in transparent mode, then
* the (ip,port) combination will already be bound for the previous application.
* In that case, the best we can do is bind with a different port.
* This does mean the local server can't use the ident protocol as the port will
* have changed, but most people won't care.
* Also note that stunnel uses the same logic for the same situation.
*/
((struct sockaddr_in *)from.ai_addr)->sin_port = 0;
res = bind(fd, from.ai_addr, from.ai_addrlen);
CHECK_RES_RETURN(res, "bind", res);
}
return 0;
}
@@ -463,6 +482,7 @@ int fd2fd(struct queue *target_q, struct queue *from_q)
return FD_NODATA;
case ECONNRESET:
case ENOTSOCK:
case EPIPE:
return FD_CNXCLOSED;
}
@@ -530,7 +550,8 @@ char* sprintaddr(char* buf, size_t size, struct addrinfo *a)
}
/* Turns a hostname and port (or service) into a list of struct addrinfo
* returns 0 on success, -1 otherwise and logs error
* On success, returns 0
* On failure, returns -1 or one of getaddrinfo() codes
*/
int resolve_split_name(struct addrinfo **out, char* host, char* serv)
{
@@ -556,7 +577,7 @@ int resolve_split_name(struct addrinfo **out, char* host, char* serv)
res = getaddrinfo(host, serv, &hint, out);
if (res)
print_message(msg_system_error, "%s `%s:%s'\n", gai_strerror(res), host, serv);
print_message(msg_system_error, "resolve_split_name: %s `%s:%s'\n", gai_strerror(res), host, serv);
return res;
}
@@ -848,4 +869,3 @@ void write_pid_file(const char* pidfile)
exit(3);
}
}
+4
View File
@@ -183,4 +183,8 @@ void start_shoveler(int);
void main_loop(struct listen_endpoint *listen_sockets, int num_addr_listen);
/* landlock.c */
void setup_landlock(void);
#endif
+9
View File
@@ -0,0 +1,9 @@
#ifndef CONFIG_H
/* Template for config.h, filled by `configure`. */
/* Landlock sandboxing Linux LSM */
#undef HAVE_LANDLOCK
#endif
Vendored Executable
+4469
View File
File diff suppressed because it is too large. Load diff
+9
View File
@@ -0,0 +1,9 @@
dnl Use autoconf to generate the `configure` script from this and Makefile.in
AC_INIT
AC_CONFIG_HEADERS(config.h)
AC_CONFIG_FILES([Makefile])
AC_CHECK_HEADERS(linux/landlock.h, AC_DEFINE(HAVE_LANDLOCK), [])
AC_OUTPUT
+9
View File
@@ -1,3 +1,12 @@
Pre-built binaries
==================
Docker images of `master` and of the tagged versions are
available directly from [Github](https://github.com/yrutschle/sslh/pkgs/container/sslh).
Windows binaries for Cygwin are graciouly produced by
nono303 on his [repository](https://github.com/nono303/sslh).
Compile and install
===================
+72
View File
@@ -0,0 +1,72 @@
It is possible to run `sslh` on Windows. The `fork` model
should be avoided as it is very inefficient on Windows, but
`sslh-select` and `sslh-ev` both work with good performance
(prefer the latter, however).
The following script downloads the latest cygwin, the latest version of sslh, and then compiles and copies the binaries with dependancies to an output folder.
It may be needed to correct it from time to time, but it works. I use it in a virtual machine.
Just retrieve WGET.EXE from [https://eternallybored.org/misc/wget/](url) or git binaries.
Copy the 3 files
GO.cmd
wget.exe
compile.sh
to C root folder, then execute **GO.cmd** with administrative rights.
with **GO.cmd**
@ECHO OFF
CD /D "%~dp0"
NET SESSION >NUL 2>&1
IF %ERRORLEVEL% NEQ 0 (
ECHO Permission denied. This script must be run as an Administrator.
ECHO:
GOTO FIN
) ELSE (
ECHO Running as Administrator.
TIMEOUT /T 2 >NUL
wget --no-check-certificate https://www.cygwin.com/setup-x86_64.exe
IF NOT EXIST setup-x86_64.exe GOTO FIN
MKDIR C:\Z
setup-x86_64.exe -l C:\Z -s ftp://ftp.funet.fi/pub/mirrors/sourceware.org/pub/cygwin/ -q -P make -P git -P gcc-g++ -P autoconf -P automake -P libtool -P libpcre-devel -P libpcre2-devel -P bison -P libev-devel
MKDIR C:\cygwin64\home\user
COPY COMPILE.SH C:\cygwin64\home\user
START C:\cygwin64\bin\mintty.exe /bin/bash --login -i ~/compile.sh
START EXPLORER C:\zzSORTIE
)
:FIN
PAUSE
EXIT
and **compile.sh**
# SAVE FILE TO UNIX FORMAT
# COPY IT IN C cygwin64 home user
git clone https://github.com/hyperrealm/libconfig.git
cd libconfig
autoreconf -fi
./configure
make
make install
cd ..
cp /usr/local/lib/libconfig.* /usr/lib
git clone https://github.com/yrutschle/sslh.git
cd sslh
make
cd ..
mkdir /cygdrive/c/zzSORTIE
cp ./sslh/sslh*.exe /cygdrive/c/zzSORTIE
cp /usr/local/bin/cygconfig-11.dll /cygdrive/c/zzSORTIE
cp /cygdrive/c/cygwin64/bin/cygwin1.dll /cygdrive/c/zzSORTIE
cp /cygdrive/c/cygwin64/bin/cygpcreposix-0.dll /cygdrive/c/zzSORTIE
cp /cygdrive/c/cygwin64/bin/cygpcre-1.dll /cygdrive/c/zzSORTIE
cp /cygdrive/c/cygwin64/bin/cygev-4.dll /cygdrive/c/zzSORTIE
cp /cygdrive/c/cygwin64/bin/cygpcre2-8-0.dll /cygdrive/c/zzSORTIE
This method was contributed by lerenardo on [github](https://github.com/yrutschle/sslh/issues/196#issuecomment-1692805639).
+2 -6
View File
@@ -99,12 +99,8 @@ Then you can run sslh-select as an unprivileged user, e.g.:
Transparent proxy support
-------------------------
Transparent proxying allows the target server to see the
original client IP address, i.e. `sslh` becomes invisible.
This makes it easier to use the server's logs, and potential
IP-based banning ability.
Set up can get complicated, so it has its own [document](tproxy.md).
Transparent proxying is described in its own
[document](tproxy.md).
Systemd Socket Activation
-------------------------
+156
View File
@@ -0,0 +1,156 @@
/*
* Setup a sandbox using the Landlock LSM, if available.
# Copyright (C) 2023 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
#
*/
#include "config.h"
#include "log.h"
#ifdef HAVE_LANDLOCK
#define _GNU_SOURCE
#include <linux/landlock.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
/* Ubuntu 22.04 does not have this symbol */
#ifndef LANDLOCK_ACCESS_FS_REFER
#define LANDLOCK_ACCESS_FS_REFER (1ULL << 13)
#endif
#ifndef landlock_create_ruleset
static inline int
landlock_create_ruleset(const struct landlock_ruleset_attr *const attr,
const size_t size, const __u32 flags)
{
return syscall(__NR_landlock_create_ruleset, attr, size, flags);
}
#endif
#ifndef landlock_add_rule
static inline int landlock_add_rule(const int ruleset_fd,
const enum landlock_rule_type rule_type,
const void *const rule_attr,
const __u32 flags)
{
return syscall(__NR_landlock_add_rule, ruleset_fd, rule_type, rule_attr,
flags);
}
#endif
#ifndef landlock_restrict_self
static inline int landlock_restrict_self(const int ruleset_fd,
const __u32 flags)
{
return syscall(__NR_landlock_restrict_self, ruleset_fd, flags);
}
#endif
typedef enum {
LL_TREE,
LL_FILE
} ll_obj_type;
static int add_path_ro(int ruleset_fd, ll_obj_type otype, const char* path)
{
int fd = open(path, O_PATH | O_CLOEXEC);
if (fd < 0) {
print_message(msg_config_error, "Landlock: Failed to open %s: %s\n", path, strerror(errno));
return -1;
}
struct landlock_path_beneath_attr path_beneath = {
.allowed_access = (otype == LL_TREE ? LANDLOCK_ACCESS_FS_READ_DIR : 0 ) |
LANDLOCK_ACCESS_FS_READ_FILE,
.parent_fd = fd,
};
int res = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, &path_beneath, 0);
if (res) {
print_message(msg_config_error, "Landlock: Failed to update the ruleset with \"%s\": %s\n",
path, strerror(errno));
close(path_beneath.parent_fd);
return -1;
}
return 0;
}
void setup_landlock(void)
{
__u64 restrict_rules =
LANDLOCK_ACCESS_FS_EXECUTE |
LANDLOCK_ACCESS_FS_READ_FILE |
LANDLOCK_ACCESS_FS_READ_DIR |
LANDLOCK_ACCESS_FS_WRITE_FILE |
LANDLOCK_ACCESS_FS_REMOVE_DIR |
LANDLOCK_ACCESS_FS_REMOVE_FILE |
LANDLOCK_ACCESS_FS_MAKE_CHAR |
LANDLOCK_ACCESS_FS_MAKE_DIR |
LANDLOCK_ACCESS_FS_MAKE_REG |
LANDLOCK_ACCESS_FS_MAKE_SOCK |
LANDLOCK_ACCESS_FS_MAKE_FIFO |
LANDLOCK_ACCESS_FS_MAKE_BLOCK |
LANDLOCK_ACCESS_FS_MAKE_SYM |
LANDLOCK_ACCESS_FS_REFER;
struct landlock_ruleset_attr ruleset_attr = {
.handled_access_fs = restrict_rules
};
/* ruleset_addr.handled_access_fs contains all rights that will be restricted
* unless explicitly added */
int ruleset_fd = landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
if (ruleset_fd < 0) {
print_message(msg_config_error, "Landlock: Failed to create a ruleset");
return;
}
/* Access to libraries, to be able to fork */
add_path_ro(ruleset_fd, LL_TREE, "/lib");
add_path_ro(ruleset_fd, LL_TREE, "/usr/lib");
add_path_ro(ruleset_fd, LL_FILE, "/etc/ld.so.cache"); /* To avoid searching all libs... */
/* Files to resolve names (required when dynamic resolution is used) */
add_path_ro(ruleset_fd, LL_FILE, "/etc/hosts");
add_path_ro(ruleset_fd, LL_FILE, "/etc/resolv.conf");
add_path_ro(ruleset_fd, LL_FILE, "/etc/nsswitch.conf");
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
print_message(msg_config_error, "Landlock: Failed to restrict privileges");
return;
}
if (landlock_restrict_self(ruleset_fd, 0)) {
print_message(msg_config_error, "Landlock: Failed to enforce ruleset");
return;
}
close(ruleset_fd);
print_message(msg_config, "Landlock: all restricted\n");
}
#else /* HAVE_LANDLOCK */
void setup_landlock(void)
{
print_message(msg_config, "Landlock: not built in\n");
return;
}
#endif /* HAVE_LANDLOCK */
+27 -4
View File
@@ -1,5 +1,5 @@
/* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README)
* on Sun Sep 11 21:43:25 2022.
* on Tue Jan 2 21:25:47 2024.
# conf2struct: generate libconf parsers that read to structs
# Copyright (C) 2018-2021 Yves Rutschle
@@ -365,7 +365,7 @@ static int clcpy(config_type type, void* target, const void* cl_arg)
return 0;
}
/* Copy the value of a string argument to arbitrary memory
/* Copy the value of a string argument to arbitary memory
* location that must be large enough, converting on the way
* (i.e. CFG_INT gets atoi() and so on) */
/* 0: success
@@ -471,6 +471,7 @@ struct arg_file* sslhcfg_conffile;
struct arg_str* sslhcfg_listen;
struct arg_str* sslhcfg_ssh;
struct arg_str* sslhcfg_tls;
struct arg_str* sslhcfg_ssl;
struct arg_str* sslhcfg_openvpn;
struct arg_str* sslhcfg_tinc;
struct arg_str* sslhcfg_wireguard;
@@ -1333,6 +1334,15 @@ static struct compound_cl_target sslhcfg_openvpn_targets [] = {
{ 0 }
};
static struct compound_cl_target sslhcfg_ssl_targets [] = {
{ & table_sslhcfg_protocols[0], 0, .value.def_string = "tls" },
{ & table_sslhcfg_protocols[1], 1, .value.def_string = "0" },
{ & table_sslhcfg_protocols[2], 2, .value.def_string = "0" },
{ & table_sslhcfg_protocols[10], 0, .value.def_int = 1 },
{ & table_sslhcfg_protocols[7], 0, .value.def_bool = 1 },
{ 0 }
};
static struct compound_cl_target sslhcfg_tls_targets [] = {
{ & table_sslhcfg_protocols[0], 0, .value.def_string = "tls" },
{ & table_sslhcfg_protocols[1], 1, .value.def_string = "0" },
@@ -1395,6 +1405,18 @@ static struct compound_cl_arg compound_cl_args[] = {
.override_const = "tls",
},
{ /* arg: ssl */
.regex = "(.+):(\\w+)",
.arg_cl = & sslhcfg_ssl,
.base_entry = & table_sslhcfg [26],
.targets = sslhcfg_ssl_targets,
.override_desc = & table_sslhcfg_protocols [0],
.override_matchindex = 0,
.override_const = "tls",
},
{ /* arg: openvpn */
.regex = "(.+):(\\w+)",
.arg_cl = & sslhcfg_openvpn,
@@ -1818,7 +1840,7 @@ static int set_target_fields(void* target_addr, struct compound_cl_arg* arg, con
if (pmatch[pmatch_cnt].rm_so == -1) {
/* This should not happen as regexec() did
* match before, unless there is a
* discrepancy between the regex and the
* discrepency between the regex and the
* number of backreferences */
return 0;
}
@@ -2111,7 +2133,7 @@ static void scalar_to_string(char** strp, config_setting_t* s)
/* Typesets all the settings in a configuration as a
* newly-allocated string. The string management is caller's
* responsibility.
* responsability.
* Returns the number of scalars in the configuration */
static int cfg_as_string(config_setting_t* parent, const char* path, char** strp)
{
@@ -2199,6 +2221,7 @@ int sslhcfg_cl_parse(int argc, char* argv[], struct sslhcfg_item* cfg)
sslhcfg_listen = arg_strn("p", "listen", "<host:port>", 0, 10, "Listen on host:port"),
sslhcfg_ssh = arg_strn(NULL, "ssh", "<host:port>", 0, 10, "Set up ssh target"),
sslhcfg_tls = arg_strn(NULL, "tls", "<host:port>", 0, 10, "Set up TLS/SSL target"),
sslhcfg_ssl = arg_strn(NULL, "ssl", "<host:port>", 0, 10, "Set up TLS/SSL target"),
sslhcfg_openvpn = arg_strn(NULL, "openvpn", "<host:port>", 0, 10, "Set up OpenVPN target"),
sslhcfg_tinc = arg_strn(NULL, "tinc", "<host:port>", 0, 10, "Set up tinc target"),
sslhcfg_wireguard = arg_strn(NULL, "wireguard", "<host:port>", 0, 10, "Set up WireGuard target"),
+1 -1
View File
@@ -1,5 +1,5 @@
/* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README)
* on Sun Sep 11 21:43:25 2022.
* on Tue Jan 2 21:25:47 2024.
# conf2struct: generate libconf parsers that read to structs
# Copyright (C) 2018-2021 Yves Rutschle
+21
View File
@@ -178,6 +178,25 @@ void tcp_listener(struct listen_endpoint* endpoint, int num_endpoints, int activ
while (1) {
in_socket = accept(endpoint[active_endpoint].socketfd, 0, 0);
CHECK_RES_RETURN(in_socket, "accept", /*void*/ );
if (in_socket == -1) {
print_message(msg_system_error, "%s:%d:%s:%d:%s\n",
__FILE__, __LINE__, "accept", errno, strerror(errno));
switch(in_socket) {
case ENETDOWN: /* accept(2) cites all these errnos as "you should retry" */
case EPROTO:
case ENOPROTOOPT:
case EHOSTDOWN:
case ENONET:
case EHOSTUNREACH:
case EOPNOTSUPP:
case ENETUNREACH:
case ECONNABORTED:
continue;
default: /* Otherwise, it's something wrong in our parameters, we fail */
return;
}
}
print_message(msg_fd, "accepted fd %d\n", in_socket);
switch(fork()) {
@@ -224,6 +243,8 @@ void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen)
print_message(msg_config_error, "UDP not (yet?) supported in sslh-fork\n");
else
tcp_listener(listen_sockets, num_addr_listen, i);
exit(0);
break;
/* We're in the parent, we don't need to do anything */
+12 -5
View File
@@ -60,20 +60,26 @@ static void printcaps(void) {
static void printsettings(void)
{
char buf[NI_MAXHOST];
char buf[NI_MAXHOST + 256]; /* 256 > " family %d %d" for reasonable ints */
int i;
struct sslhcfg_protocols_item *p;
for (i = 0; i < cfg.protocols_len; i++ ) {
p = &cfg.protocols[i];
strcpy(buf, "resolve on forward");
if (!p->resolve_on_forward) {
sprintaddr(buf, sizeof(buf), p->saddr);
int len = strlen(buf);
sprintf(buf+len, " family %d %d",
p->saddr->ai_family,
p->saddr->ai_addr->sa_family);
}
print_message(msg_config,
"%s addr: %s. libwrap service: %s log_level: %d family %d %d [%s] [%s] [%s]\n",
"%s addr: %s. libwrap service: %s log_level: %d [%s] [%s] [%s]\n",
p->name,
sprintaddr(buf, sizeof(buf), p->saddr),
buf,
p->service,
p->log_level,
p->saddr->ai_family,
p->saddr->ai_addr->sa_family,
p->keepalive ? "keepalive" : "",
p->fork ? "fork" : "",
p->transparent ? "transparent" : ""
@@ -277,6 +283,7 @@ int main(int argc, char *argv[], char* envp[])
if (cfg.user || cfg.chroot)
drop_privileges(cfg.user, cfg.chroot);
setup_landlock();
printcaps();
+13
View File
@@ -186,6 +186,19 @@ cl_groups: (
{ path: "tfo_ok"; value: 1 }
);
},
# Redundant with the --tls setting before, for backwards compatibility
{ name: "ssl"; pattern: "(.+):(\w+)"; description: "Set up TLS/SSL target";
list: "protocols";
override: "name";
argdesc: "<host:port>";
targets: (
{ path: "name"; value: "tls" },
{ path: "host"; value: "$1" },
{ path: "port"; value: "$2" },
{ path: "log_level"; value: 1 },
{ path: "tfo_ok"; value: 1 }
);
},
{ name: "openvpn"; pattern: "(.+):(\w+)"; description: "Set up OpenVPN target";
list: "protocols";
override: "name";
+4 -4
View File
@@ -14,7 +14,7 @@
use strict;
use IO::Socket::INET6;
use Data::Dumper;
use Conf::Libconfig;
use Conf::Libconfig 1.0.3;
## BEGIN TEST CONFIG
@@ -44,7 +44,7 @@ $conf->read_file("test.cfg");
# Pick one address for TCP and one for UDP
my @listen = @{$conf->fetch_array("listen")};
my @listen = @{$conf->value("listen")};
my ($sslh_tcp_address, $sslh_udp_address);
foreach my $l (@listen) {
@@ -186,7 +186,7 @@ sub udp_client {
}
}
foreach my $p (@{$conf->fetch_array("protocols")}) {
foreach my $p (@{$conf->value("protocols")}) {
if (!fork) {
my $udp = $p->{is_udp} ? "--udp" : "";
my $cmd = "./echosrv $udp -p $p->{host}:$p->{port} --prefix '$p->{name}: ' 2> /dev/null";
@@ -203,7 +203,7 @@ sleep 2; # Let echosrv's and sslh start
my ($c_in, $c_out);
pipe $c_in, $c_out;
my @protocols = @{$conf->fetch_array("protocols")};
my @protocols = @{$conf->value("protocols")};
if (!fork) {
# Process that starts all the clients
+2 -1
View File
@@ -42,7 +42,7 @@ listen:
protocols:
(
{ name: "ssh"; host: "localhost"; port: "9000"; fork: true; transparent: true; },
{ name: "ssh"; host: "localhost"; port: "9000"; fork: true; transparent: true; resolve_on_forward: true; },
{ name: "socks5"; host: "localhost"; port: "9001"; },
{ name: "http"; host: "localhost"; port: "9002"; },
{ name: "tinc"; host: "localhost"; port: "9003"; },
@@ -53,6 +53,7 @@ protocols:
{ name: "regex"; host: "ip4-localhost"; is_udp: true; port: "9020";
udp_timeout: 30;
regex_patterns: [ "^foo" ];
resolve_on_forward: true;
},
{ name: "regex"; host: "localhost"; port: "9011";
regex_patterns: [ "^foo", "^bar" ];
+14 -6
View File
@@ -36,10 +36,10 @@
static int cnx_cmp(struct connection* cnx1, struct connection* cnx2)
{
struct sockaddr* addr1 = &cnx1->client_addr;
struct sockaddr_storage* addr1 = &cnx1->client_addr;
socklen_t addrlen1 = cnx1->addrlen;
struct sockaddr* addr2 = &cnx2->client_addr;
struct sockaddr_storage* addr2 = &cnx2->client_addr;
socklen_t addrlen2 = cnx2->addrlen;
if (addrlen1 != addrlen2) return -1;
@@ -52,13 +52,13 @@ static int cnx_cmp(struct connection* cnx1, struct connection* cnx2)
* lowest bytes of remote port */
static int hash_make_key(hash_item new)
{
struct sockaddr* addr = &new->client_addr;
struct sockaddr_storage* addr = &new->client_addr;
//socklen_t addrlen = new->addrlen;
struct sockaddr_in* addr4;
struct sockaddr_in6* addr6;
int out;
switch (addr->sa_family) {
switch (((struct sockaddr*)addr)->sa_family) {
case AF_INET:
addr4 = (struct sockaddr_in*)addr;
out = addr4->sin_port;
@@ -225,8 +225,16 @@ static void mark_active(struct connection* cnx)
/* Creates a new non-blocking socket */
static int nonblocking_socket(struct sslhcfg_protocols_item* proto)
{
int res;
if (proto->resolve_on_forward) {
res = resolve_split_name(&(proto->saddr), proto->host,
proto->port);
if (res) return -1;
}
int out = socket(proto->saddr->ai_family, SOCK_DGRAM, 0);
int res = set_nonblock(out);
res = set_nonblock(out);
if (res == -1) {
print_message(msg_system_error, "%s:%d:%s:%d:%s\n", __FILE__, __LINE__, "udp:socket:nonblock", errno, strerror(errno));
close(out);
@@ -322,7 +330,7 @@ void udp_s2c_forward(struct connection* cnx)
if ((res == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) return;
CHECK_RES_DIE(res, "udp_listener/recvfrom");
res = sendto(cnx->local_endpoint, data, res, 0,
&cnx->client_addr, cnx->addrlen);
(struct sockaddr*)&cnx->client_addr, cnx->addrlen);
mark_active(cnx);
}