mirror of
https://github.com/yrutschle/sslh.git
synced 2026-10-22 18:12:10 +03:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e831b732db | ||
|
|
8166be1a09 | ||
|
|
684374f353 | ||
|
|
de7351fd84 | ||
|
|
d2ca706f86 | ||
|
|
c859e341aa | ||
|
|
717fe8fae5 | ||
|
|
1ddf45bf52 | ||
|
|
ae7530e33f | ||
|
|
d0a016221c | ||
|
|
9286b55702 | ||
|
|
31c9e19abb | ||
|
|
ef8f3d1419 | ||
|
|
2759c223be | ||
|
|
b3c770898a | ||
|
|
fee8491a8e | ||
|
|
a80d79fd40 | ||
|
|
4b921be69d | ||
|
|
1799a81079 | ||
|
|
5f1c1b1b61 | ||
|
|
58783af410 | ||
|
|
1957be1dc3 | ||
|
|
ecca78bde7 | ||
|
|
b94060ad76 | ||
|
|
736b108a75 | ||
|
|
7ca567fcd9 | ||
|
|
3117c15fbd | ||
|
|
e428fc505c | ||
|
|
4dfb4d300a | ||
|
|
39184b5622 | ||
|
|
940461de18 | ||
|
|
6f949419d1 | ||
|
|
dab5df7409 | ||
|
|
402ca5219b | ||
|
|
046401148d | ||
|
|
780e536aeb | ||
|
|
ed0ab12a16 | ||
|
|
b65f1e8b26 | ||
|
|
91b649daa0 | ||
|
|
7499c26e9e | ||
|
|
90a55b6f9d | ||
|
|
1f66e2e093 | ||
|
|
92d2326016 | ||
|
|
81eed9d56a | ||
|
|
490a44723b | ||
|
|
23fb1eba6f | ||
|
|
be66848e2d | ||
|
|
3e93c1d43d | ||
|
|
8930ec395e |
No files matched your search
@@ -3,8 +3,15 @@
|
||||
*.o
|
||||
cscope.*
|
||||
echosrv
|
||||
libsslh.a
|
||||
sslh-fork
|
||||
sslh-select
|
||||
sslh-ev
|
||||
systemd-sslh-generator
|
||||
sslh.8.gz
|
||||
tags
|
||||
version.h
|
||||
/config.status
|
||||
/config.log
|
||||
/config.h
|
||||
/Makefile
|
||||
@@ -1,3 +1,20 @@
|
||||
v2.1.2:
|
||||
Fix inetd mode.
|
||||
|
||||
v2.1.1:
|
||||
Various minor fixes.
|
||||
|
||||
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.
|
||||
|
||||
+1
-1
@@ -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
@@ -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]
|
||||
@@ -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](doc/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
|
||||
|
||||
@@ -11,9 +11,13 @@ pidfile: "/var/run/sslh.pid";
|
||||
|
||||
# Change hostname with your external address name, or the IP
|
||||
# of the interface that receives connections
|
||||
# Default is to bind all interfaces. httpd can be started
|
||||
# first to bind on localhost, in which case sslh will bind
|
||||
# only other interfaces.
|
||||
listen:
|
||||
(
|
||||
{ host: "thelonious"; port: "443"; }
|
||||
{ host: "0.0.0.0"; port: "443"; },
|
||||
{ host: "[::]"; port: "443"; }
|
||||
);
|
||||
|
||||
|
||||
@@ -24,8 +28,6 @@ protocols:
|
||||
(
|
||||
{ name: "ssh"; service: "ssh"; host: "localhost"; port: "22"; fork: true; },
|
||||
{ name: "openvpn"; host: "localhost"; port: "1194"; },
|
||||
{ name: "xmpp"; host: "localhost"; port: "5222"; },
|
||||
{ name: "http"; host: "localhost"; port: "80"; },
|
||||
{ name: "tls"; host: "localhost"; port: "443"; log_level: 0; },
|
||||
{ name: "anyprot"; host: "localhost"; port: "443"; }
|
||||
);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -372,7 +391,7 @@ int connect_addr(struct connection *cnx, int fd_from, connect_blocking blocking)
|
||||
}
|
||||
|
||||
/* Store some data to write to the queue later */
|
||||
int defer_write(struct queue *q, void* data, int data_size)
|
||||
int defer_write(struct queue *q, void* data, ssize_t data_size)
|
||||
{
|
||||
char *p;
|
||||
ptrdiff_t data_offset = q->deferred_data - q->begin_deferred_data;
|
||||
@@ -384,7 +403,7 @@ int defer_write(struct queue *q, void* data, int data_size)
|
||||
q->begin_deferred_data = p;
|
||||
q->deferred_data = p + data_offset;
|
||||
p += data_offset + q->deferred_data_size;
|
||||
q->deferred_data_size += data_size;
|
||||
q->deferred_data_size += (int)data_size;
|
||||
memcpy(p, data, data_size);
|
||||
|
||||
return 0;
|
||||
@@ -396,13 +415,13 @@ int defer_write(struct queue *q, void* data, int data_size)
|
||||
* */
|
||||
int flush_deferred(struct queue *q)
|
||||
{
|
||||
int n;
|
||||
ssize_t n;
|
||||
|
||||
print_message(msg_fd, "flushing deferred data to fd %d\n", q->fd);
|
||||
|
||||
n = write(q->fd, q->deferred_data, q->deferred_data_size);
|
||||
if (n == -1)
|
||||
return n;
|
||||
return (int)n;
|
||||
|
||||
if (n == q->deferred_data_size) {
|
||||
/* All has been written -- release the memory */
|
||||
@@ -413,10 +432,10 @@ int flush_deferred(struct queue *q)
|
||||
} else {
|
||||
/* There is data left */
|
||||
q->deferred_data += n;
|
||||
q->deferred_data_size -= n;
|
||||
q->deferred_data_size -= (int)n;
|
||||
}
|
||||
|
||||
return n;
|
||||
return (int)n;
|
||||
}
|
||||
|
||||
|
||||
@@ -451,7 +470,8 @@ void dump_connection(struct connection *cnx)
|
||||
int fd2fd(struct queue *target_q, struct queue *from_q)
|
||||
{
|
||||
char buffer[BUFSIZ];
|
||||
int target, from, size_r, size_w;
|
||||
int target, from;
|
||||
ssize_t size_r, size_w;
|
||||
|
||||
target = target_q->fd;
|
||||
from = from_q->fd;
|
||||
@@ -463,6 +483,7 @@ int fd2fd(struct queue *target_q, struct queue *from_q)
|
||||
return FD_NODATA;
|
||||
|
||||
case ECONNRESET:
|
||||
case ENOTSOCK:
|
||||
case EPIPE:
|
||||
return FD_CNXCLOSED;
|
||||
}
|
||||
@@ -495,7 +516,7 @@ int fd2fd(struct queue *target_q, struct queue *from_q)
|
||||
|
||||
CHECK_RES_RETURN(size_w, "write", FD_CNXCLOSED);
|
||||
|
||||
return size_w;
|
||||
return (int)size_w;
|
||||
}
|
||||
|
||||
/* returns a string that prints the IP and port of the sockaddr */
|
||||
@@ -530,7 +551,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 +578,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;
|
||||
}
|
||||
|
||||
@@ -832,20 +854,19 @@ void write_pid_file(const char* pidfile)
|
||||
|
||||
f = fopen(pidfile, "w");
|
||||
if (!f) {
|
||||
print_message(msg_system_error, "write_pid_file:%s:%s", pidfile, strerror(errno));
|
||||
exit(3);
|
||||
print_message(msg_system_error, "write_pid_file: %s: %s\n", pidfile, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
res = fprintf(f, "%d\n", getpid());
|
||||
if (res < 0) {
|
||||
print_message(msg_system_error, "write_pid_file:fprintf:%s", strerror(errno));
|
||||
exit(3);
|
||||
print_message(msg_system_error, "write_pid_file: fprintf: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
res = fclose(f);
|
||||
if (res == EOF) {
|
||||
print_message(msg_system_error, "write_pid_file:fclose:%s", strerror(errno));
|
||||
exit(3);
|
||||
print_message(msg_system_error, "write_pid_file: fclose: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
* enough for the macros to adapt (http://support.microsoft.com/kb/111855)
|
||||
*/
|
||||
#ifdef __CYGWIN__
|
||||
#undef FD_SETSIZE
|
||||
#define FD_SETSIZE 4096
|
||||
#endif
|
||||
|
||||
@@ -171,7 +172,7 @@ int resolve_split_name(struct addrinfo **out, char* hostname, char* port);
|
||||
|
||||
int start_listen_sockets(struct listen_endpoint *sockfd[]);
|
||||
|
||||
int defer_write(struct queue *q, void* data, int data_size);
|
||||
int defer_write(struct queue *q, void* data, ssize_t data_size);
|
||||
int flush_deferred(struct queue *q);
|
||||
|
||||
extern struct sslhcfg_item cfg;
|
||||
@@ -183,4 +184,8 @@ void start_shoveler(int);
|
||||
|
||||
void main_loop(struct listen_endpoint *listen_sockets, int num_addr_listen);
|
||||
|
||||
/* landlock.c */
|
||||
void setup_landlock(void);
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,9 @@
|
||||
|
||||
|
||||
#ifndef CONFIG_H
|
||||
/* Template for config.h, filled by `configure`. */
|
||||
|
||||
/* Landlock sandboxing Linux LSM */
|
||||
#undef HAVE_LANDLOCK
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large.
Load diff
@@ -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
|
||||
+84
-83
@@ -1,3 +1,13 @@
|
||||
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 graciously produced by
|
||||
nono303 on his [repository](https://github.com/nono303/sslh).
|
||||
|
||||
|
||||
Compile and install
|
||||
===================
|
||||
|
||||
@@ -6,139 +16,130 @@ Dependencies
|
||||
|
||||
`sslh` uses:
|
||||
|
||||
* [libconfig](http://www.hyperrealm.com/libconfig/). For
|
||||
Debian this is contained in package `libconfig-dev`. You
|
||||
can compile with or without it using USELIBCONFIG in the
|
||||
Makefile.
|
||||
* [libconfig](http://www.hyperrealm.com/libconfig/).
|
||||
For Debian this is contained in package `libconfig-dev`.
|
||||
You can compile with or without it using USELIBCONFIG in the Makefile.
|
||||
|
||||
* [libwrap](http://packages.debian.org/source/unstable/tcp-wrappers).
|
||||
For Debian, this is contained in packages
|
||||
`libwrap0-dev`. You
|
||||
can compile with or without it using USELIBWRAP in the
|
||||
Makefile.
|
||||
For Debian, this is contained in packages `libwrap0-dev`.
|
||||
You can compile with or without it using USELIBWRAP in the Makefile.
|
||||
|
||||
* [libsystemd](http://packages.debian.org/source/unstable/libsystemd-dev), in package `libsystemd-dev`. You
|
||||
can compile with or without it using USESYSTEMD in the
|
||||
Makefile.
|
||||
* [libsystemd](http://packages.debian.org/source/unstable/libsystemd-dev), in package `libsystemd-dev`.
|
||||
You can compile with or without it using USESYSTEMD in the Makefile.
|
||||
|
||||
* [libcap](http://packages.debian.org/source/unstable/libcap-dev), in package `libcap-dev`. You can compile with or without it using USELIBCAP in the Makefile
|
||||
* [libcap](http://packages.debian.org/source/unstable/libcap-dev), in package `libcap-dev`.
|
||||
You can compile with or without it using USELIBCAP in the Makefile
|
||||
|
||||
* libbsd, to enable to change the process name (as shown in
|
||||
`ps`, so each forked process shows what protocol and what
|
||||
connection it is serving),
|
||||
which requires `libbsd` at runtime, and `libbsd-dev` at
|
||||
compile-time.
|
||||
* libbsd, to enable to change the process name (as shown in `ps`,
|
||||
so each forked process shows what protocol and what connection it is serving),
|
||||
which requires `libbsd` at runtime, and `libbsd-dev` at compile-time.
|
||||
|
||||
* libpcre2, in package `libpcre-dev`. You can compile
|
||||
with or without it using ENABLE_REGEX in the Makefile.
|
||||
* libpcre2, in package `libpcre2-dev`.
|
||||
You can compile with or without it using ENABLE_REGEX in the Makefile.
|
||||
|
||||
* libev-dev, in package `libev-dev`. If you build a binary
|
||||
specifically and do not build `sslh-ev`, you don't need
|
||||
this.
|
||||
* libev-dev, in package `libev-dev`.
|
||||
If you build a binary specifically and do not build `sslh-ev`, you don't need this.
|
||||
|
||||
|
||||
For OpenSUSE, these are contained in packages libconfig9 and
|
||||
libconfig-dev in repository
|
||||
<http://download.opensuse.org/repositories/multimedia:/libs/openSUSE_12.1/>
|
||||
|
||||
For Fedora, you'll need packages `libconfig` and
|
||||
`libconfig-devel`:
|
||||
For Fedora, you'll need packages `libconfig` and `libconfig-devel`:
|
||||
|
||||
yum install libconfig libconfig-devel
|
||||
yum install libconfig libconfig-devel
|
||||
|
||||
If you want to rebuild `sslh-conf.c` (after a `make
|
||||
distclean` for example), you will also need to add
|
||||
[conf2struct](https://www.rutschle.net/tech/conf2struct/README.html)
|
||||
If you want to rebuild `sslh-conf.c` (after a `make distclean` for example),
|
||||
you will also need to add [conf2struct](https://www.rutschle.net/tech/conf2struct/README.html)
|
||||
(v1.5) to your path.
|
||||
|
||||
|
||||
The test scripts are written in Perl, and will require
|
||||
IO::Socket::INET6 (libio-socket-inet6-perl in Debian).
|
||||
`IO::Socket::INET6` (`libio-socket-inet6-perl` in Debian).
|
||||
|
||||
|
||||
Compilation
|
||||
-----------
|
||||
|
||||
After this, the Makefile should work:
|
||||
|
||||
make install
|
||||
make install
|
||||
|
||||
There are a couple of configuration options at the beginning
|
||||
of the Makefile:
|
||||
There are a couple of configuration options at the beginning of the Makefile:
|
||||
|
||||
* `USELIBWRAP` compiles support for host access control (see
|
||||
`hosts_access(3)`), you will need `libwrap` headers and
|
||||
library to compile (`libwrap0-dev` in Debian).
|
||||
* `USELIBWRAP` compiles support for host access control (see `hosts_access(3)`),
|
||||
you will need `libwrap` headers and library to compile (`libwrap0-dev` in Debian).
|
||||
|
||||
* `USELIBCONFIG` compiles support for the configuration
|
||||
file. You will need `libconfig` headers to compile
|
||||
(`libconfig8-dev` in Debian).
|
||||
* `USELIBCONFIG` compiles support for the configuration file.
|
||||
You will need `libconfig` headers to compile (`libconfig8-dev` in Debian).
|
||||
|
||||
* `USESYSTEMD` compiles support for using systemd socket activation.
|
||||
You will need `systemd` headers to compile (`systemd-devel` in Fedora).
|
||||
* `USESYSTEMD` compiles support for using systemd socket activation.
|
||||
You will need `systemd` headers to compile (`systemd-devel` in Fedora).
|
||||
|
||||
* `USELIBBSD` compiles support for updating the process name (as shown by `ps`).
|
||||
|
||||
* `USELIBBSD` compiles support for updating the process name (as shown
|
||||
by `ps`).
|
||||
|
||||
Generating the configuration parser
|
||||
-----------------------------------
|
||||
|
||||
The configuration file and command line parser is generated
|
||||
by `conf2struct`, from `sslhconf.cfg`, which generates
|
||||
`sslh-conf.c` and `sslh-conf.h`. The resulting files are
|
||||
included in the source so `sslh` can be built without
|
||||
`conf2struct` installed.
|
||||
The configuration file and command line parser is generated by `conf2struct`,
|
||||
from `sslhconf.cfg`, which generates `sslh-conf.c` and `sslh-conf.h`.
|
||||
The resulting files are included in the source
|
||||
so `sslh` can be built without `conf2struct` installed.
|
||||
|
||||
Further, to prevent build issues,
|
||||
`sslh-conf.[ch]` has no dependency to `sslhconf.cfg` in the Makefile.
|
||||
In the event of adding configuration settings,
|
||||
they need to be regenerated using `make c2s`.
|
||||
|
||||
Further, to prevent build issues, `sslh-conf.[ch]` has no
|
||||
dependency to `sslhconf.cfg` in the Makefile. In the event
|
||||
of adding configuration settings, they need to be
|
||||
regenerated using `make c2s`.
|
||||
|
||||
Binaries
|
||||
--------
|
||||
|
||||
The Makefile produces three different executables: `sslh-fork`,
|
||||
`sslh-select` and `sslh-ev`:
|
||||
The Makefile produces three different executables:
|
||||
`sslh-fork`, `sslh-select` and `sslh-ev`:
|
||||
|
||||
* `sslh-fork` forks a new process for each incoming connection.
|
||||
It is well-tested and very reliable, but incurs the overhead
|
||||
of many processes.
|
||||
If you are going to use `sslh` for a "small" setup (less than
|
||||
a dozen ssh connections and a low-traffic https server) then
|
||||
`sslh-fork` is probably more suited for you.
|
||||
It is well-tested and very reliable, but incurs the overhead of many processes.
|
||||
If you are going to use `sslh` for a "small" setup
|
||||
(less than a dozen ssh connections and a low-traffic https server)
|
||||
then `sslh-fork` is probably more suited for you.
|
||||
|
||||
* `sslh-select` uses only one thread, which monitors all
|
||||
connections at once. It only incurs a 16 byte overhead per
|
||||
connection. Also, if it stops, you'll lose all connections,
|
||||
which means you can't upgrade it remotely. If you are going
|
||||
to use `sslh` on a "medium" setup (a few hundreds of
|
||||
connections), or if you are on a system where forking is
|
||||
expensive (e.g. Windows), `sslh-select` will be better.
|
||||
* `sslh-select` uses only one thread, which monitors all connections at once.
|
||||
It only incurs a 16 byte overhead per connection.
|
||||
Also, if it stops, you'll lose all connections,
|
||||
which means you can't upgrade it remotely.
|
||||
If you are going to use `sslh` on a "medium" setup (a few hundreds of connections),
|
||||
or if you are on a system where forking is expensive (e.g. Windows),
|
||||
`sslh-select` will be better.
|
||||
|
||||
* `sslh-ev` is similar to `sslh-select`, but uses `libev` as a backend.
|
||||
This allows using specific kernel APIs that
|
||||
allow to manage thousands of connections concurrently.
|
||||
|
||||
* `sslh-ev` is similar to `sslh-select`, but uses `libev` as
|
||||
a backend. This allows using specific kernel APIs that
|
||||
allow to manage thousands of connections concurrently.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
* In general:
|
||||
|
||||
make
|
||||
cp sslh-fork /usr/local/sbin/sslh
|
||||
cp basic.cfg /etc/sslh.cfg
|
||||
vi /etc/sslh.cfg
|
||||
|
||||
```sh
|
||||
./configure
|
||||
make
|
||||
cp sslh-fork /usr/local/sbin/sslh
|
||||
cp basic.cfg /etc/sslh.cfg
|
||||
vi /etc/sslh.cfg
|
||||
```
|
||||
* For Debian:
|
||||
|
||||
cp scripts/etc.init.d.sslh /etc/init.d/sslh
|
||||
|
||||
```sh
|
||||
cp scripts/etc.init.d.sslh /etc/init.d/sslh
|
||||
```
|
||||
* For CentOS:
|
||||
|
||||
cp scripts/etc.rc.d.init.d.sslh.centos /etc/rc.d/init.d/sslh
|
||||
|
||||
```sh
|
||||
cp scripts/etc.rc.d.init.d.sslh.centos /etc/rc.d/init.d/sslh
|
||||
```
|
||||
|
||||
You might need to create links in /etc/rc<x>.d so that the server
|
||||
start automatically at boot-up, e.g. under Debian:
|
||||
|
||||
update-rc.d sslh defaults
|
||||
```sh
|
||||
update-rc.d sslh defaults
|
||||
```
|
||||
|
||||
@@ -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/ 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
@@ -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
|
||||
-------------------------
|
||||
|
||||
@@ -62,9 +62,10 @@ void check_res_dump(int res, struct addrinfo *addr, char* syscall)
|
||||
|
||||
void start_echo(int fd)
|
||||
{
|
||||
int res;
|
||||
ssize_t res;
|
||||
char buffer[1 << 20];
|
||||
int ret, prefix_len;
|
||||
ssize_t ret;
|
||||
size_t prefix_len;
|
||||
int first = 1;
|
||||
|
||||
prefix_len = strlen(cfg.prefix);
|
||||
@@ -151,7 +152,7 @@ void udp_echo(struct listen_endpoint* listen_socket)
|
||||
|
||||
while (1) {
|
||||
addrlen = sizeof(src_addr);
|
||||
size_t len = recvfrom(listen_socket->socketfd,
|
||||
ssize_t len = recvfrom(listen_socket->socketfd,
|
||||
data + prefix_len,
|
||||
sizeof(data) - prefix_len,
|
||||
0,
|
||||
@@ -166,7 +167,7 @@ void udp_echo(struct listen_endpoint* listen_socket)
|
||||
|
||||
print_udp_xchange(listen_socket->socketfd, &src_addr, addrlen);
|
||||
|
||||
int res = sendto(listen_socket->socketfd,
|
||||
ssize_t res = sendto(listen_socket->socketfd,
|
||||
data,
|
||||
len + prefix_len,
|
||||
0,
|
||||
|
||||
+3
-1
@@ -78,7 +78,9 @@ listen:
|
||||
# connect to the first that matches.)
|
||||
#
|
||||
# tls:
|
||||
# sni_hostnames: list of FQDN for that target
|
||||
# sni_hostnames: list of FQDN for that target. Each name can
|
||||
# include wildcard following glob(7) rules.
|
||||
|
||||
# alpn_protocols: list of ALPN protocols for that target, see:
|
||||
# https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids
|
||||
#
|
||||
|
||||
+156
@@ -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 */
|
||||
@@ -323,7 +323,7 @@ static int is_adb_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_
|
||||
if (len < min_data_packet_size + sizeof(empty_message))
|
||||
return PROBE_AGAIN;
|
||||
|
||||
if (memcmp(&p[0], empty_message, sizeof(empty_message)))
|
||||
if (memcmp(&p[0], empty_message, sizeof(empty_message)) != 0)
|
||||
return PROBE_NEXT;
|
||||
|
||||
return probe_adb_cnxn_message(&p[sizeof(empty_message)]);
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
[Unit]
|
||||
Description=SSL/SSH multiplexer (select mode) for %I
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
EnvironmentFile=/etc/default/sslh
|
||||
ExecStart=/usr/sbin/sslh-select -F/etc/sslh/%I.cfg -f $DAEMON_OPTS
|
||||
KillMode=process
|
||||
#Hardening
|
||||
PrivateTmp=true
|
||||
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
|
||||
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||||
SecureBits=noroot-locked
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
ProtectKernelModules=true
|
||||
ProtectKernelTunables=true
|
||||
ProtectControlGroups=true
|
||||
MountFlags=private
|
||||
NoNewPrivileges=true
|
||||
PrivateDevices=true
|
||||
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
|
||||
MemoryDenyWriteExecute=true
|
||||
DynamicUser=true
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -1,10 +1,10 @@
|
||||
[Unit]
|
||||
Description=SSL/SSH multiplexer
|
||||
Description=SSL/SSH multiplexer (fork mode) for %I
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
EnvironmentFile=/etc/conf.d/sslh
|
||||
ExecStart=/usr/sbin/sslh --foreground $DAEMON_OPTS
|
||||
EnvironmentFile=/etc/default/sslh
|
||||
ExecStart=/usr/sbin/sslh -F/etc/sslh/%I.cfg -f $DAEMON_OPTS
|
||||
KillMode=process
|
||||
#Hardening
|
||||
PrivateTmp=true
|
||||
+32
-6
@@ -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"),
|
||||
@@ -2216,10 +2239,13 @@ int sslhcfg_cl_parse(int argc, char* argv[], struct sslhcfg_item* cfg)
|
||||
/* Parse command line */
|
||||
nerrors = arg_parse(argc, argv, argtable);
|
||||
if (nerrors) {
|
||||
arg_print_errors(stdout, sslhcfg_end, "sslhcfg");
|
||||
// print bad args
|
||||
arg_print_errors(stdout, sslhcfg_end, "sslhcfg");
|
||||
// print usage
|
||||
arg_print_syntax(stdout, argtable, "\n");
|
||||
// print options
|
||||
arg_print_glossary(stdout, argtable, " %-25s\t%s\n");
|
||||
return -1;
|
||||
fprintf(stderr, "Invalid args are ignored, please fix them\n");
|
||||
}
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+28
@@ -164,6 +164,13 @@ void set_listen_procname(struct listen_endpoint *listen_socket)
|
||||
}
|
||||
|
||||
|
||||
/* At least MacOS does not know these two options, so define them to something
|
||||
* equivalent for our use case */
|
||||
#ifndef ENONET
|
||||
#define ENONET EWOULDBLOCK
|
||||
#endif
|
||||
/* /MacOS kludge */
|
||||
|
||||
/* TCP listener: connections, fork a child for each new connection
|
||||
* IN:
|
||||
* endpoint: array of listening endpoint objects
|
||||
@@ -178,6 +185,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 +250,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 */
|
||||
|
||||
+7
-3
@@ -37,6 +37,7 @@
|
||||
#include "common.h"
|
||||
#include "probe.h"
|
||||
#include "log.h"
|
||||
#include "tcp-probe.h"
|
||||
|
||||
/* Constants for options that have no one-character shorthand */
|
||||
#define OPT_ONTIMEOUT 257
|
||||
@@ -69,7 +70,7 @@ static void printsettings(void)
|
||||
strcpy(buf, "resolve on forward");
|
||||
if (!p->resolve_on_forward) {
|
||||
sprintaddr(buf, sizeof(buf), p->saddr);
|
||||
int len = strlen(buf);
|
||||
size_t len = strlen(buf);
|
||||
sprintf(buf+len, " family %d %d",
|
||||
p->saddr->ai_family,
|
||||
p->saddr->ai_addr->sa_family);
|
||||
@@ -98,7 +99,8 @@ static void printsettings(void)
|
||||
static void setup_regex_probe(struct sslhcfg_protocols_item *p)
|
||||
#ifdef ENABLE_REGEX
|
||||
{
|
||||
int num_patterns, i, error;
|
||||
size_t num_patterns, i;
|
||||
int error;
|
||||
pcre2_code** pattern_list;
|
||||
PCRE2_SIZE error_offset;
|
||||
PCRE2_UCHAR8 err_str[120];
|
||||
@@ -186,7 +188,7 @@ void config_sanity_check(struct sslhcfg_item* cfg)
|
||||
#endif
|
||||
|
||||
for (i = 0; i < cfg->protocols_len; ++i) {
|
||||
if (strcmp(cfg->protocols[i].name, "tls")) {
|
||||
if (strcmp(cfg->protocols[i].name, "tls") != 0) {
|
||||
if (cfg->protocols[i].sni_hostnames_len) {
|
||||
print_message(msg_config_error, "name: \"%s\"; host: \"%s\"; port: \"%s\": "
|
||||
"Config option sni_hostnames is only applicable for tls\n",
|
||||
@@ -245,6 +247,7 @@ int main(int argc, char *argv[], char* envp[])
|
||||
if (cfg.inetd)
|
||||
{
|
||||
close(fileno(stderr)); /* Make sure no error will go to client */
|
||||
tcp_init();
|
||||
start_shoveler(0);
|
||||
exit(0);
|
||||
}
|
||||
@@ -283,6 +286,7 @@ int main(int argc, char *argv[], char* envp[])
|
||||
|
||||
if (cfg.user || cfg.chroot)
|
||||
drop_privileges(cfg.user, cfg.chroot);
|
||||
setup_landlock();
|
||||
|
||||
printcaps();
|
||||
|
||||
|
||||
+2
-2
@@ -67,7 +67,7 @@ static void watchers_init(watchers** w, struct listen_endpoint* listen_sockets,
|
||||
void watchers_add_read(watchers* w, int fd)
|
||||
{
|
||||
FD_SET(fd, &w->fds_r);
|
||||
if (fd > w->max_fd)
|
||||
if (fd + 1 > w->max_fd)
|
||||
w->max_fd = fd + 1;
|
||||
}
|
||||
|
||||
@@ -148,7 +148,7 @@ void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen)
|
||||
|
||||
print_message(msg_fd, "selecting... max_fd=%d num_probing=%d\n",
|
||||
fd_info.watchers->max_fd, fd_info.num_probing);
|
||||
res = select(fd_info.watchers->max_fd + 1, &readfds, &writefds,
|
||||
res = select(fd_info.watchers->max_fd, &readfds, &writefds,
|
||||
NULL, fd_info.num_probing ? &tv : NULL);
|
||||
if (res < 0)
|
||||
perror("select");
|
||||
|
||||
@@ -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";
|
||||
|
||||
+251
-89
@@ -1,39 +1,72 @@
|
||||
#include <libconfig.h>
|
||||
#include <dirent.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/stat.h>
|
||||
#include "common.h"
|
||||
|
||||
|
||||
#define print_message(sink, format, file, line) fprintf(stderr, format, file, line)
|
||||
#define print_message(sink, format, file, line) fprintf(stderr, format, file, line)
|
||||
|
||||
static char* resolve_listen(const char *hostname, const char *port) {
|
||||
/* Need room in the strcat for \0 and :
|
||||
* the format in the socket unit file is hostname:port */
|
||||
char *conn = malloc(strlen(hostname)+strlen(port)+2);
|
||||
CHECK_ALLOC(conn, "malloc");
|
||||
typedef struct FileList FileList;
|
||||
|
||||
struct FileList {
|
||||
char *name;
|
||||
struct FileList *prev;
|
||||
};
|
||||
|
||||
static void free_file_list(FileList *fl) {
|
||||
while (fl != NULL) {
|
||||
FileList *prev = fl->prev;
|
||||
free(fl->name);
|
||||
free(fl);
|
||||
fl = prev;
|
||||
}
|
||||
}
|
||||
|
||||
static FILE *err_log;
|
||||
static bool systemd_invoked = false;
|
||||
static const char
|
||||
*socket_ext = ".socket",
|
||||
*dropin_ext = ".conf",
|
||||
*service_ext = ".service.d/",
|
||||
*fork_unit_file = "/sslh@",
|
||||
*select_unit_file = "/sslh-select@",
|
||||
*fork_unit = "# Automatically generated by systemd-sslh-generator\n\n"
|
||||
"[Unit]\n"
|
||||
"Requires=sslh@%s.socket\n"
|
||||
"Conflicts=sslh-select@%s.service\n"
|
||||
"PartOf=sslh@%s.socket\n",
|
||||
*select_unit = "# Automatically generated by systemd-sslh-generator\n\n"
|
||||
"[Unit]\n"
|
||||
"Requires=sslh@%s.socket\n"
|
||||
"Conflicts=sslh@%s.service\n"
|
||||
"PartOf=sslh@%s.socket\n";
|
||||
|
||||
static char *resolve_listen(const char *hostname, const char *port) {
|
||||
char *conn = calloc(1, strlen(hostname) + strlen(port) + 2);
|
||||
CHECK_ALLOC(conn, "malloc")
|
||||
strcpy(conn, hostname);
|
||||
strcat(conn, ":");
|
||||
strcat(conn, port);
|
||||
|
||||
return conn;
|
||||
|
||||
}
|
||||
|
||||
|
||||
static int get_listen_from_conf(const char *filename, char **listen[]) {
|
||||
config_t config;
|
||||
config_setting_t *setting, *addr;
|
||||
const char *hostname, *port;
|
||||
int len = 0;
|
||||
|
||||
/* look up the listen stanzas in the config file so these
|
||||
* can be used in the socket file generated */
|
||||
config_init(&config);
|
||||
if (config_read_file(&config, filename) == CONFIG_FALSE) {
|
||||
/* we don't care if file is missing, skip it */
|
||||
if (config_error_line(&config) != 0) {
|
||||
fprintf(stderr, "%s:%d:%s\n",
|
||||
fprintf(err_log,
|
||||
"systemd-sslh-generator: %s%d%s\n",
|
||||
filename,
|
||||
config_error_line(&config),
|
||||
config_error_text(&config));
|
||||
@@ -42,118 +75,247 @@ static int get_listen_from_conf(const char *filename, char **listen[]) {
|
||||
} else {
|
||||
setting = config_lookup(&config, "listen");
|
||||
if (setting) {
|
||||
int i;
|
||||
len = config_setting_length(setting);
|
||||
*listen = malloc(len * sizeof(**listen));
|
||||
CHECK_ALLOC(*listen, "malloc");
|
||||
for (i = 0; i < len; i++) {
|
||||
CHECK_ALLOC(*listen, "malloc")
|
||||
for (int i = 0; i < len; i++) {
|
||||
addr = config_setting_get_elem(setting, i);
|
||||
if (! (config_setting_lookup_string(addr, "host", &hostname) &&
|
||||
config_setting_lookup_string(addr, "port", &port))) {
|
||||
fprintf(stderr,
|
||||
"line %d:Incomplete specification (hostname and port required)\n",
|
||||
if (!(config_setting_lookup_string(addr, "host", &hostname) && config_setting_lookup_string(addr, "port", &port))) {
|
||||
fprintf(err_log,
|
||||
"systemd-sslh-generator: line %d:Incomplete specification (hostname and port required)\n",
|
||||
config_setting_source_line(addr));
|
||||
return -1;
|
||||
} else {
|
||||
char *resolved_listen = resolve_listen(hostname, port);
|
||||
|
||||
(*listen)[i] = malloc(strlen(resolved_listen));
|
||||
CHECK_ALLOC((*listen)[i], "malloc");
|
||||
strcpy((*listen)[i], resolved_listen);
|
||||
free(resolved_listen);
|
||||
(*listen)[i] = resolve_listen(hostname, port);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
|
||||
}
|
||||
|
||||
static int write_socket_unit(FILE *socket, char *listen[], int num_addr, const char *source) {
|
||||
int i;
|
||||
|
||||
static void write_socket_unit(FILE *socket, char *listen[], int num_addr, const char *cfg, const char *source) {
|
||||
fprintf(socket,
|
||||
"# Automatically generated by systemd-sslh-generator\n\n"
|
||||
"[Unit]\n"
|
||||
"Before=sslh.service\n"
|
||||
"Before=sslh@%s.service\n"
|
||||
"SourcePath=%s\n"
|
||||
"PartOf=sslh@%s.service\n"
|
||||
"Documentation=man:sslh(8) man:systemd-sslh-generator(8)\n\n"
|
||||
"[Socket]\n"
|
||||
"FreeBind=true\n",
|
||||
source);
|
||||
|
||||
for (i = 0; i < num_addr; i++) {
|
||||
cfg,
|
||||
source,
|
||||
cfg);
|
||||
for (int i = 0; i < num_addr; i++) {
|
||||
fprintf(socket, "ListenStream=%s\n", listen[i]);
|
||||
}
|
||||
fprintf(socket,
|
||||
"\n[Install]\n"
|
||||
"WantedBy=sockets.target\n");
|
||||
}
|
||||
|
||||
static int write_unit_dropin(const char *runtime_unit_dir, const bool is_fork, const char *cfg) {
|
||||
const char
|
||||
*unit_file = is_fork ? fork_unit_file : select_unit_file,
|
||||
*unit_string = is_fork ? fork_unit : select_unit;
|
||||
FILE *dropin_fd = stdout;
|
||||
if (systemd_invoked) {
|
||||
//Systemd drop-in configuration for the base select service sslh@%I.service
|
||||
const size_t runtime_len = strlen(runtime_unit_dir);
|
||||
size_t len = strlen(unit_file) + strlen(cfg) + strlen(service_ext) + strlen(dropin_ext);
|
||||
char dropin_dir[runtime_len + len + 1];
|
||||
strcpy(dropin_dir, runtime_unit_dir);
|
||||
strcat(dropin_dir, unit_file);
|
||||
strcat(dropin_dir, cfg);
|
||||
strcat(dropin_dir, service_ext);
|
||||
|
||||
if (mkdir(dropin_dir, S_IRWXU | S_IRWXG | S_IROTH)) {
|
||||
fprintf(err_log,
|
||||
"systemd-sslh-generator: Could not create directory '%s' to generate drop-in configuration: %s\n",
|
||||
dropin_dir,
|
||||
strerror(errno));
|
||||
return errno;
|
||||
}
|
||||
|
||||
len = len + strlen(cfg) + strlen(dropin_ext);
|
||||
char dropin_path[len + 1];
|
||||
strcpy(dropin_path, dropin_dir);
|
||||
strcat(dropin_path, cfg);
|
||||
strcat(dropin_path, dropin_ext);
|
||||
dropin_fd = fopen(dropin_path, "w");
|
||||
if (!dropin_fd) {
|
||||
fprintf(err_log,
|
||||
"systemd-sslh-generator: Could not open '%s' to generate drop-in configuration: %s\n",
|
||||
dropin_path,
|
||||
strerror(errno));
|
||||
return errno;
|
||||
}
|
||||
}
|
||||
fprintf(dropin_fd, unit_string, cfg, cfg, cfg);
|
||||
if (systemd_invoked) {
|
||||
fclose(dropin_fd);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gen_sslh_config(char *runtime_unit_dir) {
|
||||
char *sslh_conf;
|
||||
int num_addr;
|
||||
FILE *config;
|
||||
int status = 0;
|
||||
const char *config_dir = "/etc/sslh/";
|
||||
char **listen;
|
||||
FILE *runtime_conf_fd = stdout;
|
||||
const char *unit_file;
|
||||
DIR *d = opendir(config_dir);
|
||||
FileList *fa = NULL;
|
||||
|
||||
/* There are two default locations so check both with first given preference */
|
||||
sslh_conf = "/etc/sslh.cfg";
|
||||
|
||||
config = fopen(sslh_conf, "r");
|
||||
if (config == NULL) {
|
||||
sslh_conf="/etc/sslh/sslh.cfg";
|
||||
config = fopen(sslh_conf, "r");
|
||||
if (config == NULL) {
|
||||
return -1;
|
||||
if (d) {
|
||||
struct dirent *dir;
|
||||
while ((dir = readdir(d)) != NULL) {
|
||||
if ((strcmp(dir->d_name, ".") == 0) || (strcmp(dir->d_name, "..") == 0)) {
|
||||
continue;
|
||||
}
|
||||
FileList *lfa = malloc(sizeof(FileList));
|
||||
CHECK_ALLOC(lfa, "malloc")
|
||||
lfa->name = malloc(strlen(dir->d_name) + 1);
|
||||
CHECK_ALLOC(lfa->name, "malloc")
|
||||
strcpy(lfa->name, dir->d_name);
|
||||
lfa->prev = NULL;
|
||||
if (fa) {
|
||||
lfa->prev = fa;
|
||||
}
|
||||
fa = lfa;
|
||||
}
|
||||
closedir(d);
|
||||
} else {
|
||||
fprintf(err_log,
|
||||
"systemd-sslh-generator: Configuration directory '/etc/sslh/' does not exist! No units generated.\n");
|
||||
//Config directory /etc/sslh/ does not exist
|
||||
return 0;
|
||||
}
|
||||
|
||||
fclose(config);
|
||||
|
||||
num_addr = get_listen_from_conf(sslh_conf, &listen);
|
||||
if (num_addr < 0)
|
||||
return -1;
|
||||
|
||||
/* If this is run by systemd directly write to the location told to
|
||||
* otherwise write to standard out so that it's trivial to check what
|
||||
* will be written */
|
||||
if (runtime_unit_dir && *runtime_unit_dir) {
|
||||
unit_file = "/sslh.socket";
|
||||
size_t uf_len = strlen(unit_file);
|
||||
size_t runtime_len = strlen(runtime_unit_dir) + uf_len + 1;
|
||||
char *runtime_conf = malloc(runtime_len);
|
||||
CHECK_ALLOC(runtime_conf, "malloc");
|
||||
strcpy(runtime_conf, runtime_unit_dir);
|
||||
strcat(runtime_conf, unit_file);
|
||||
runtime_conf_fd = fopen(runtime_conf, "w");
|
||||
free(runtime_conf);
|
||||
if (!fa) {
|
||||
fprintf(err_log,
|
||||
"systemd-sslh-generator: Configuration directory '/etc/sslh/' is empty! No units generated.\n");
|
||||
//No configuration files in /etc/sslh/
|
||||
return 0;
|
||||
}
|
||||
FileList *fa_ref = fa;
|
||||
// size_t num_listen_addresses = 0;
|
||||
// char **listen_addresses = NULL;
|
||||
//Process all config files
|
||||
do {
|
||||
char *end = strstr(fa->name, ".cfg");
|
||||
if (!end) {
|
||||
continue;
|
||||
}
|
||||
//Current sslh config name
|
||||
const size_t end_count = end - fa->name;
|
||||
char config_name[end_count + 1];
|
||||
memcpy(config_name, fa->name, end_count);
|
||||
config_name[end_count] = '\0';
|
||||
|
||||
|
||||
return write_socket_unit(runtime_conf_fd, listen, num_addr, sslh_conf);
|
||||
//Full path to current sslh config
|
||||
char full_path[strlen(config_dir) + strlen(fa->name) + 1];
|
||||
strcpy(full_path, config_dir);
|
||||
strcat(full_path, fa->name);
|
||||
FILE *config = fopen(full_path, "r");
|
||||
if (!config) {
|
||||
fprintf(err_log,
|
||||
"systemd-sslh-generator: Could not open config file '%s': %s\n",
|
||||
full_path,
|
||||
strerror(errno));
|
||||
return errno;
|
||||
} else {
|
||||
fclose(config);
|
||||
int num_addr = get_listen_from_conf(full_path, &listen);
|
||||
if (num_addr <= 0) {
|
||||
fprintf(err_log,
|
||||
"systemd-sslh-generator: sslh config '%s' contains no valid listen configurations!\n",
|
||||
fa->name);
|
||||
status |= -1;
|
||||
continue;
|
||||
}
|
||||
FILE *socket_fd = stdout;
|
||||
if (systemd_invoked) {
|
||||
//Systemd socket for the current sslh config
|
||||
const size_t len = strlen(runtime_unit_dir) + strlen(fork_unit_file) + strlen(config_name) + strlen(socket_ext);
|
||||
char socket_path[len + 1];
|
||||
strcpy(socket_path, runtime_unit_dir);
|
||||
strcat(socket_path, fork_unit_file);
|
||||
strcat(socket_path, config_name);
|
||||
strcat(socket_path, socket_ext);
|
||||
socket_fd = fopen(socket_path, "w");
|
||||
if (!socket_fd) {
|
||||
fprintf(err_log,
|
||||
"systemd-sslh-generator: Could not open '%s' to generate socket configuration: %s\n",
|
||||
socket_path,
|
||||
strerror(errno));
|
||||
status |= errno;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
//Write socket unit
|
||||
write_socket_unit(socket_fd, listen, num_addr, config_name, full_path);
|
||||
//Write forking drop-in config
|
||||
status |= write_unit_dropin(runtime_unit_dir, false, config_name);
|
||||
//Write select drop-in config
|
||||
status |= write_unit_dropin(runtime_unit_dir, true, config_name);
|
||||
if (systemd_invoked) {
|
||||
fclose(socket_fd);
|
||||
}
|
||||
// if (listen_addresses) {
|
||||
// //Check for overlapping addresses
|
||||
// for (size_t i = 0; i < num_listen_addresses; i++) {
|
||||
// for (size_t j = 0; j < num_addr; j++) {
|
||||
// if (strcmp(*(listen_addresses + i), *(listen + j)) == 0) {
|
||||
// fprintf(err_log, "systemd-sslh-generator: Overlapping listen addresses across sslh configurations!");
|
||||
// return -1;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// char *listen_addresses_copy[num_listen_addresses + num_addr];
|
||||
// if (listen_addresses) {
|
||||
// //Copy previous configurations' listen addresses into temp
|
||||
// memcpy(listen_addresses_copy, listen_addresses, num_listen_addresses);
|
||||
// //Free global list of listen addresses
|
||||
// free(listen_addresses);
|
||||
// }
|
||||
// //Copy listen addresses from current configuration into temp
|
||||
// memcpy(listen_addresses_copy + num_listen_addresses, listen, num_addr);
|
||||
// num_listen_addresses += num_addr;
|
||||
// listen_addresses = malloc(num_listen_addresses * sizeof(char *));
|
||||
// CHECK_ALLOC(listen_addresses, "malloc")
|
||||
// //Append all addresses to global list
|
||||
// memcpy(listen_addresses, listen_addresses_copy, num_listen_addresses);
|
||||
//Free all allocated listen strings
|
||||
for (size_t i = 0; i < num_addr; i++) {
|
||||
free(*(listen + i));
|
||||
}
|
||||
}
|
||||
} while((fa = fa->prev));
|
||||
free_file_list(fa_ref);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[]){
|
||||
int r = 0;
|
||||
int k;
|
||||
char *runtime_unit_dest = "";
|
||||
|
||||
if (argc > 1 && (argc != 4) ) {
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc == 1 || argc == 4) {
|
||||
systemd_invoked = argc == 4;
|
||||
if (systemd_invoked) {
|
||||
err_log = fopen("/dev/kmsg", "w");
|
||||
if (!err_log) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
err_log = stderr;
|
||||
}
|
||||
const int r = gen_sslh_config(systemd_invoked ? argv[1] : "");
|
||||
if (systemd_invoked) {
|
||||
fclose(err_log);
|
||||
}
|
||||
if (!r) {
|
||||
fprintf(err_log, "systemd-sslh-generator: Successfully generated all targets.\n");
|
||||
}
|
||||
return r < 0 ? -1 : 0;
|
||||
} else {
|
||||
printf("This program takes three or no arguments.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (argc > 1)
|
||||
runtime_unit_dest = argv[1];
|
||||
|
||||
k = gen_sslh_config(runtime_unit_dest);
|
||||
if (k < 0)
|
||||
r = k;
|
||||
|
||||
return r < 0 ? -1 : 0;
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -60,13 +60,13 @@ int probe_client_protocol(struct connection *cnx)
|
||||
|
||||
static void tcp_protocol_list_init(void)
|
||||
{
|
||||
tcp_protocols = calloc(cfg.protocols_len, sizeof(tcp_protocols));
|
||||
CHECK_ALLOC(tcp_protocols, "tcp_protocols");
|
||||
for (int i = 0; i < cfg.protocols_len; i++) {
|
||||
struct sslhcfg_protocols_item* p = &cfg.protocols[i];
|
||||
if (!p->is_udp) {
|
||||
tcp_protocols[tcp_protocols_len] = p;
|
||||
tcp_protocols_len++;
|
||||
tcp_protocols = realloc(tcp_protocols, tcp_protocols_len * sizeof(*tcp_protocols));
|
||||
CHECK_ALLOC(tcp_protocols, "realloc");
|
||||
tcp_protocols[tcp_protocols_len-1] = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,7 +224,7 @@ parse_server_name_extension(const struct TLSProtocol *tls_data, const char *data
|
||||
switch (data[pos]) { /* name type */
|
||||
case 0x00: /* host_name */
|
||||
if(has_match(tls_data->sni_hostname_list, tls_data->sni_list_len, data + pos + 3, len)) {
|
||||
return len;
|
||||
return (int)len;
|
||||
} else {
|
||||
return TLS_ENOEXT;
|
||||
}
|
||||
@@ -253,7 +253,7 @@ parse_alpn_extension(const struct TLSProtocol *tls_data, const char *data, size_
|
||||
return TLS_EPROTOCOL;
|
||||
|
||||
if (len > 0 && has_match(tls_data->alpn_protocol_list, tls_data->alpn_list_len, data + pos + 1, len)) {
|
||||
return len;
|
||||
return (int)len;
|
||||
} else if (len > 0) {
|
||||
print_message(msg_probe_error, "Unknown ALPN name: %.*s\n", (int)len, data + pos + 1);
|
||||
}
|
||||
@@ -301,11 +301,11 @@ struct TLSProtocol *
|
||||
tls_data_set_list(struct TLSProtocol *tls_data, int alpn, const char** list, size_t list_len) {
|
||||
if (alpn) {
|
||||
tls_data->alpn_protocol_list = list;
|
||||
tls_data->alpn_list_len = list_len;
|
||||
tls_data->alpn_list_len = (int)list_len;
|
||||
tls_data->match_mode.tls_match_alpn = 1;
|
||||
} else {
|
||||
tls_data->sni_hostname_list = list;
|
||||
tls_data->sni_list_len = list_len;
|
||||
tls_data->sni_list_len = (int)list_len;
|
||||
tls_data->match_mode.tls_match_sni = 1;
|
||||
}
|
||||
|
||||
|
||||
+15
-11
@@ -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,13 +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) {
|
||||
resolve_split_name(&(proto->saddr), proto->host,
|
||||
proto->port);
|
||||
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);
|
||||
@@ -256,7 +259,8 @@ struct connection* udp_c2s_forward(int sockfd, struct loop_info* fd_info)
|
||||
struct connection* cnx;
|
||||
ssize_t len;
|
||||
socklen_t addrlen;
|
||||
int res, target, out = -1;
|
||||
ssize_t res;
|
||||
int target, out = -1;
|
||||
char data[65536]; /* Theoretical max is 65507 (https://en.wikipedia.org/wiki/User_Datagram_Protocol).
|
||||
This will do. Dynamic allocation is possible with the MSG_PEEK flag in recvfrom(2), but that'd imply
|
||||
malloc/free overhead for each packet, when really 64K is not that much */
|
||||
@@ -277,7 +281,7 @@ struct connection* udp_c2s_forward(int sockfd, struct loop_info* fd_info)
|
||||
len, target, sprintaddr(addr_str, sizeof(addr_str), &addrinfo));
|
||||
|
||||
if (target == -1) {
|
||||
res = probe_buffer(data, len, udp_protocols, udp_protocols_len, &proto);
|
||||
res = probe_buffer(data, (int)len, udp_protocols, udp_protocols_len, &proto);
|
||||
/* First version: if we can't work out the protocol from the first
|
||||
* packet, drop it. Conceivably, we could store several packets to
|
||||
* run probes on packet sets */
|
||||
@@ -321,13 +325,13 @@ void udp_s2c_forward(struct connection* cnx)
|
||||
{
|
||||
int sockfd = cnx->target_sock;
|
||||
char data[65536];
|
||||
int res;
|
||||
ssize_t res;
|
||||
|
||||
res = recvfrom(sockfd, data, sizeof(data), 0, NULL, NULL);
|
||||
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);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user