diff --git a/Dockerfile b/Dockerfile index 156075d..cb8de3a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,7 @@ RUN \ libconfig-dev \ make \ musl-dev \ - pcre2-dev \ + pcre-dev \ perl && \ cd /sslh && \ make sslh-select && \ @@ -18,6 +18,6 @@ FROM alpine:latest COPY --from=build /sslh/sslh-select /sslh -RUN apk --no-cache add libconfig pcre2 +RUN apk --no-cache add libconfig pcre ENTRYPOINT [ "/sslh", "--foreground"] diff --git a/Makefile b/Makefile index 1381e33..d8ed217 100644 --- a/Makefile +++ b/Makefile @@ -27,13 +27,9 @@ CC ?= gcc CFLAGS ?=-Wall -DLIBPCRE -g $(CFLAGS_COV) LIBS=-lm -lpcre2-8 - -# making sslh-conf.o also makes sslh-conf.h with -# conf2struct, which may be required by other headers: it -# should be kept first OBJS=sslh-conf.o common.o log.o sslh-main.o probe.o tls.o argtable3.o collection.o gap.o -FORK_OBJS=$(OBJS) sslh-fork.o -SELECT_OBJS=$(OBJS) sslh-select.o processes.o udp-listener.o +FORK_OBJS=sslh-fork.o $(OBJS) +SELECT_OBJS=sslh-select.o $(OBJS) processes.o udp-listener.o EV_OBJS=sslh-ev.o $(OBJS) processes.o udp-listener.o CONDITIONAL_TARGETS= @@ -79,7 +75,7 @@ version.h: sslh: sslh-fork sslh-select sslh-ev -$(OBJS): version.h common.h collection.h sslh-conf.h gap.h processes.h +$(OBJS): version.h common.h collection.h sslh-conf.h gap.h sslh-conf.c sslh-conf.h: sslhconf.cfg conf2struct sslhconf.cfg diff --git a/common.c b/common.c index 0e48cdf..1be58e9 100644 --- a/common.c +++ b/common.c @@ -554,6 +554,36 @@ int resolve_split_name(struct addrinfo **out, char* host, char* serv) return res; } +/* turns a "hostname:port" string into a list of struct addrinfo; +out: list of newly allocated addrinfo (see getaddrinfo(3)); freeaddrinfo(3) when done +fullname: input string -- it gets clobbered +*/ +void resolve_name(struct addrinfo **out, char* fullname) +{ + char *serv, *host; + int res; + + /* Find port */ + char *sep = strrchr(fullname, ':'); + if (!sep) { /* No separator: parameter is just a port */ + print_message(msg_config_error, "%s: names must be fully specified as hostname:port\n", fullname); + exit(1); + } + serv = sep+1; + *sep = 0; + + host = fullname; + + res = resolve_split_name(out, host, serv); + if (res) { + print_message(msg_config_error, "%s `%s'\n", gai_strerror(res), fullname); + if (res == EAI_SERVICE) + print_message(msg_config_error, "(Check you have specified all ports)\n"); + exit(4); + } +} + + /* Fills a connection description; returns 0 on failure */ int get_connection_desc(struct connection_desc* desc, const struct connection *cnx) { diff --git a/common.h b/common.h index 424236f..ee53dd1 100644 --- a/common.h +++ b/common.h @@ -35,6 +35,30 @@ #include "version.h" +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + + +#define CHECK_RES_DIE(res, str) \ + if (res == -1) { \ + print_message(msg_system_error, "%s:%d:", __FILE__, __LINE__); \ + perror(str); \ + exit(1); \ + } + +#define CHECK_RES_RETURN(res, str, ret) \ + if (res == -1) { \ + print_message(msg_system_error, "%s:%d:%s:%d:%s\n", __FILE__, __LINE__, str, errno, strerror(errno)); \ + return ret; \ + } + +#define CHECK_ALLOC(a, str) \ + if (!a) { \ + print_message(msg_system_error, "%s:%d:", __FILE__, __LINE__); \ + perror(str); \ + exit(1); \ + } + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) #if 1 #define TRACE fprintf(stderr, "%s:%d\n", __FILE__, __LINE__); @@ -59,6 +83,9 @@ enum connection_state { ST_SHOVELING /* Connexion is established */ }; +/* this is used to pass protocols through the command-line parameter parsing */ +#define PROT_SHIFT 1000 /* protocol options will be 1000, 1001, etc */ + /* A 'queue' is composed of a file descriptor (which can be read from or * written to), and a queue for deferred write data */ struct queue { @@ -116,7 +143,6 @@ typedef enum { BLOCKING = 1 } connect_blocking; -#include "log.h" /* common.c */ void init_cnx(struct connection *cnx); @@ -126,9 +152,11 @@ int fd2fd(struct queue *target, struct queue *from); char* sprintaddr(char* buf, size_t size, struct addrinfo *a); void resolve_name(struct addrinfo **out, char* fullname); int get_connection_desc(struct connection_desc* desc, const struct connection *cnx); +void log_connection(struct connection_desc* desc, const struct connection *cnx); void set_proctitle_shovel(struct connection_desc* desc, const struct connection *cnx); int check_access_rights(int in_socket, const char* service); void setup_signals(void); +void setup_syslog(const char* bin_name); void drop_privileges(const char* user_name, const char* chroot_path); void set_capabilities(int cap_net_admin); void write_pid_file(const char* pidfile); @@ -149,29 +177,4 @@ void start_shoveler(int); void main_loop(struct listen_endpoint *listen_sockets, int num_addr_listen); -#define MAX(a, b) (((a) > (b)) ? (a) : (b)) - - -#define CHECK_RES_DIE(res, str) \ - if (res == -1) { \ - print_message(msg_system_error, "%s:%d:", __FILE__, __LINE__); \ - perror(str); \ - exit(1); \ - } - -#define CHECK_RES_RETURN(res, str, ret) \ - if (res == -1) { \ - print_message(msg_system_error, "%s:%d:%s:%d:%s\n", __FILE__, __LINE__, str, errno, strerror(errno)); \ - return ret; \ - } - -#define CHECK_ALLOC(a, str) \ - if (!a) { \ - print_message(msg_system_error, "%s:%d:", __FILE__, __LINE__); \ - perror(str); \ - exit(1); \ - } - -#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) - #endif diff --git a/doc/INSTALL.md b/doc/INSTALL.md index e174cc6..b06859e 100644 --- a/doc/INSTALL.md +++ b/doc/INSTALL.md @@ -23,8 +23,6 @@ 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 -* [libev](http://software.schmorp.de/pkg/libev.html), in package `libev-dev`. It is only required to build `sslh-ev`, so you don't need if you only build `sslh-fork` and/or `sslh-select`. - * 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), @@ -73,8 +71,8 @@ of the Makefile: Binaries -------- -The Makefile produces three different executables: `sslh-fork`, -`sslh-select` and `sslh-ev`: +The Makefile produces two different executables: `sslh-fork` +and `sslh-select`: * `sslh-fork` forks a new process for each incoming connection. It is well-tested and very reliable, but incurs the overhead @@ -91,10 +89,10 @@ If you are going to use `sslh` on a "medium" setup (a few thousand ssh connections, and another few thousand ssl connections), `sslh-select` will be better. -* `sslh-ev` works very much like `sslh-select`, but uses - `libev` as a backend. `libev` provides more portability -and alternative ways to support very large numbers of -connections. +If you have a very large site (tens of thousands of connections), +you'll need a vapourware version that would use libevent or +something like that. + Installation ------------ diff --git a/scripts/systemd.sslh.service b/scripts/systemd.sslh.service index 9dcd49c..a6a6bb9 100644 --- a/scripts/systemd.sslh.service +++ b/scripts/systemd.sslh.service @@ -22,7 +22,6 @@ PrivateDevices=true RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX MemoryDenyWriteExecute=true DynamicUser=true -Type=forking [Install] WantedBy=multi-user.target diff --git a/systemd-sslh-generator.c b/systemd-sslh-generator.c index 6d6a44f..315e207 100644 --- a/systemd-sslh-generator.c +++ b/systemd-sslh-generator.c @@ -2,13 +2,7 @@ #include #include #include - -#define CHECK_ALLOC(a, str) \ - if (!a) { \ - fprintf(stderr, "%s:%d:", __FILE__, __LINE__); \ - perror(str); \ - exit(1); \ - } +#include "common.h" static char* resolve_listen(const char *hostname, const char *port) { diff --git a/test.cfg b/test.cfg index 2bb0edc..c8354c3 100644 --- a/test.cfg +++ b/test.cfg @@ -14,17 +14,17 @@ syslog_facility: "auth"; # Value: 1: stdout; 2: syslog; 3: both # Defaults should be sensible. Generally, you want *-error # to be always enabled, to know if something is going wrong. -verbose-config: 3; # print configuration at startup -verbose-config-error: 3; # print configuration errors -verbose-connections: 3; # trace established incoming address to forward address -verbose-connections-error: 3; # connection errors -verbose-connections-try: 3; # connection attempts towards targets -verbose-fd: 3; # file descriptor activity, open/close/whatnot -verbose-packets: 3; # hexdump packets on which probing is done -verbose-probe-info: 3; # what's happening during the probe process -verbose-probe-error: 3; # failures and problems during probing -verbose-system-error: 3; # system call problem, i.e. malloc, fork, failing -verbose-int-error: 3; # internal errors, the kind that should never happen +verbose-config: 1; # print configuration at startup +verbose-config-error: 1; # print configuration errors +verbose-connections: 1; # trace established incoming address to forward address +verbose-connections-error: 1; # connection errors +verbose-connections-try: 1; # connection attempts towards targets +verbose-fd: 1; # file descriptor activity, open/close/whatnot +verbose-packets: 1; # hexdump packets on which probing is done +verbose-probe-info: 1; # what's happening during the probe process +verbose-probe-error: 1; # failures and problems during probing +verbose-system-error: 1; # system call problem, i.e. malloc, fork, failing +verbose-int-error: 1; # internal errors, the kind that should never happen # List of interfaces on which we should listen # Options: @@ -32,7 +32,7 @@ listen: ( { host: "localhost"; port: "8080"; keepalive: true; }, { host: "localhost"; port: "8081"; keepalive: true; }, - { host: "127.0.0.1"; is_udp: true; port: "8086"; } + { host: "ip4-localhost"; is_udp: true; port: "8086"; } ); @@ -46,7 +46,7 @@ protocols: { name: "xmpp"; host: "localhost"; port: "9009"; }, { name: "adb"; host: "localhost"; port: "9010"; }, { name: "syslog"; host: "localhost"; port: "9013"; }, - { name: "ssh"; host: "127.0.0.1"; is_udp: true; port: "9020"; + { name: "regex"; host: "ip4-localhost"; is_udp: true; port: "9020"; udp_timeout: 30; regex_patterns: [ "^foo" ]; }, diff --git a/tls.c b/tls.c index fb088ed..9d56edb 100644 --- a/tls.c +++ b/tls.c @@ -255,7 +255,7 @@ parse_alpn_extension(const struct TLSProtocol *tls_data, const char *data, size_ if (len > 0 && has_match(tls_data->alpn_protocol_list, tls_data->alpn_list_len, data + pos + 1, len)) { return len; } else if (len > 0) { - print_message(msg_probe_info, "Not in ALPN list: %.*s\n", (int)len, data + pos + 1); + print_message(msg_probe_error, "Unknown ALPN name: %.*s\n", (int)len, data + pos + 1); } pos += 1 + len; } @@ -277,7 +277,7 @@ has_match(const char** list, size_t list_len, const char* name, size_t name_len) for (i = 0; i < list_len; i++) { item = &list[i]; - print_message(msg_probe_info, "matching [%.*s] with [%s]\n", (int)name_len, name, *item); + print_message(msg_probe_error, "matching [%.*s] with [%s]\n", (int)name_len, name, *item); if(!fnmatch(*item, name_nullterminated, 0)) { free(name_nullterminated); return 1;