Compare commits

...
744 Commits
Author SHA1 Message Date
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 Rutschle 93600d1fb1 Merge pull request #299 from amake/patch-1
Allow supplying additional CFLAGS
2023-08-31 15:26:42 +02:00
Yves Rutschle 04f258e705 Merge branch 'master' into patch-1 2023-08-31 15:26:22 +02:00
Yves Rutschle 1fb888bbf2 remove magic numbers with correct sizeof 2023-08-31 15:18:22 +02:00
Yves Rutschle 1f0cab2aee Merge pull request #342 from utoni/fix/ovpn-probe-read-overflow
fix possible buffer overflow
2023-08-31 15:15:50 +02:00
Yves Rutschle ff9328fa6c Merge branch 'utoni-fix/cppcheck-complains' 2023-08-31 15:13:57 +02:00
Yves Rutschle e941e8dd2e fix cppcheck complains 2023-08-31 15:10:53 +02:00
Yves Rutschle 54fe4b2f47 Merge pull request #401 from rnhmjoj/master
fix handling of IPv6 UDP connections
2023-08-26 21:32:22 +02:00
rnhmjoj 33129481cf fix handling of IPv6 UDP connections
Problem:
IPv6 addresses are 4 bytes long and don't fit inside a `sockaddr`, so
`recvfrom` will truncate the address to the first half.
When generating a reply, the remaining half of the address is filled
with garbage and the packet is subsequently delivered to the wrong host,
if not immediately dropped.

Solution:
replace `sockaddr` with `sockaddr_storage`, the latter is guaranteed to
be large enough to hold an IPv6 address and pointers can be cast to
`sockaddr *` when needed.
2023-08-22 11:44:09 +02:00
Yves Rűtschlé 4cc0867753 remove dependency of sslh-conf.c to sslhconf.cfg (fix #283) 2023-08-13 10:38:39 +02:00
Yves Rutschle 4728730abc Merge pull request #392 from klementng/docker/transparent-patch
Docker: ipv6 transparent patch
2023-08-10 08:21:39 +02:00
clement 9e7b4b751f sync and resolve merge conflict 2023-08-09 23:36:01 +08:00
Clement b11f2620ab Add clarification on --transparent mode for docker 2023-08-09 22:57:47 +08:00
Yves Rutschle 056c283145 Merge pull request #393 from oliv3r/dev/cleanup
container: Cleanup some style issues
2023-08-09 08:25:22 +02:00
Olliver Schinagl 00beb9595d container: Cleanup some style issues
Commit 5635dc5142 ("Enable --transparent mode for docker") made a
little bit of a mess of the Dockerfile and container-entrypoint.sh.

A few issues are, but not limited to; trailing whitespaces, incorrect
indentation, removed final newline, component sortability just to name a
few.

This MR fixes that and cleans up those files again.

One thing not touched was the enable/disablement of `set +e` to exit the
script on error. It is nicer/cleaner to solve this in a different way,
but that adds to much complexity.

While here, make the container architecture and alpine version
configurable, allowing us to build multi-arch images from the CI in the
future.

Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
2023-08-08 09:01:51 +02:00
clement 27f3e9075a update examples 2023-08-08 08:11:08 +08:00
clement 3912330040 add proper ipv6 checking 2023-08-08 08:02:54 +08:00
Yves Rutschle 4cbff962db Merge pull request #386 from oliv3r/dev/fix_version_tag
version: Do not put a slash in the version tag
2023-07-30 23:12:32 +02:00
Yves Rutschle 29c949e051 Merge pull request #389 from jsoref/spelling
Spelling
2023-07-30 23:09:03 +02:00
Yves Rutschle e8a84e6c22 Merge pull request #388 from klementng/master
Enable --transparent mode for docker
2023-07-30 23:01:38 +02:00
Josh Soref 42476d3cdc spelling: version
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:13 -04:00
Josh Soref f48f74c004 spelling: unprivileged
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:13 -04:00
Josh Soref 3af02d5c44 spelling: transparent
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:13 -04:00
Josh Soref 9e6d5cc13a spelling: theoretical
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref 74fe57147b spelling: tentative
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref 9acec69b9a spelling: successful
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref 2a81ec0650 spelling: subsequent
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref 596a4ee5b4 spelling: shortopts
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref dbfeb480fe spelling: return
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref 6b8a203691 spelling: responsibility
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref 95e351e150 spelling: privileges
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref 4b4c585107 spelling: parenthesis
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref 0217e842d2 spelling: overridden
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref 877ef1d27c spelling: other
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref b1bfd5aee3 spelling: nonexistent
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref af38544316 spelling: listening
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref d4908178bf spelling: initialized
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref 9deedec029 spelling: initialised
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref 8ef3e8ddd8 spelling: increase
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref 1eeba07396 spelling: inconsistency
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref 2c48b8d83b spelling: functionally
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref 44bac28718 spelling: freebind
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref f520c616cc spelling: explicitly
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref 39de82ae13 spelling: echosrv
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref 08bea0e15d spelling: discrepancy
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref cb52f3cdb4 spelling: deleterious
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref 306164531f spelling: defining
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref 40ddc4900a spelling: command
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref 32aaacf4e7 spelling: argument
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref a6a73f4d32 spelling: arbitrary
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref 3d959eb425 spelling: address
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:43:12 -04:00
Josh Soref b2fd9d9daf spelling: github
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2023-07-30 01:15:36 -04:00
clement 8ff27e931f Change ordering for better layer caching 2023-07-22 06:46:00 +08:00
Clement ecbc33ba29 Update github build link 2023-07-21 07:07:25 +08:00
clement 5635dc5142 Enable --transparent mode for docker 2023-07-21 06:58:18 +08:00
Yves Rutschle bb76bc1d31 Merge pull request #382 from oliv3r/add_default_entrypoint
docker: Add proper entrypoint
2023-06-10 19:17:12 +02:00
Yves Rutschle 4a5ccb75b2 Merge pull request #384 from oliv3r/fix/parallel_builds
CI: Do not parallelized container builds
2023-06-10 19:15:39 +02:00
Olliver Schinagl 6672cc0f86 version: Do not put a slash in the version tag
Many systems do not like having a `/` in the version tag. In some cases,
we generate a version as `head/branch`, which even gets amplified if one
uses `dev/feature` as a branch name.

So lets drop these slashes to avoid potential issues.

Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
2023-06-09 14:00:30 +02:00
Olliver Schinagl 9dd560493a container: Drop privileges
A container is best served with the least amount of privileges. This
also ensures we don't have to drop anything later.

This does require running the container with elevated capabilities.

Note, that if for whatever reason, 'root' access within the container is
needed, this can easily be accomplished by running the container with
`docker run --user root:root sslh` for example.

Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
2023-06-05 22:50:06 +02:00
Olliver Schinagl db5ed29fa2 docker: Add proper entrypoint
As per docker guidelines [0] a container should always really have a
consistent entrypoint, without having to override it or do special
tricks.

The behavior should be _identical_ as before, but will no longer trigger
errors because sslh doesn't understand certain parameters (/bin/sh
for example being common). Further more, allows a proper entrypoint for
a CI to work easily with the container as well. Allowing for scenario's
such as `apk add git && sslh --foreground` in your sslh image for example.

E.g. `docker run sslh --help` works though with the default
`--foreground` a bit weirdly, as does `docker run sslh
/bin/sh` or `docker run sslh ls`.

[0]: https://github.com/docker-library/official-images#consistency

Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
2023-06-05 22:50:06 +02:00
Olliver Schinagl 295dba93b5 docker: Do not foreground by default, this should be a choice
It is weird that when invoking sslh, that it daemonizess in foreground by
default. This should always be a user choice, and if not, it should be a
program default.

Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
2023-06-05 22:50:06 +02:00
Olliver Schinagl f23da1fc36 CI: Do not parallelized container builds
Container builds that are parallelized must be 'merged' again
afterwards. Because that makes the pipeline far more complex for a quick
compile job, we might as well just run in sequentially.

Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
2023-06-05 22:26:30 +02:00
Olliver Schinagl 20764074cb docker: Improve caching layers
Docker is most efficient if you can 'order' the layers from
least-changing to most changing to improve on cache hits.

While here, change ADD to COPY as add is really intended to download
external packages, as well as installing sslh into a proper location.

Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
2023-06-05 22:05:24 +02:00
Yves Rutschle 7b7c9231b0 Merge pull request #383 from oliv3r/dev/pipeline
docker: Automatically build and push container
2023-06-05 20:53:35 +02:00
Olliver Schinagl 397f672248 docker: Automatically build and push container
The current sslh container works fine, but needs to be created manually
and locally by the user. Instead, let the pipeline do the dirty work and
push it to this repo's own registry.

Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
2023-06-05 17:09:50 +02:00
Yves Rutschle 3707c5b8a6 fix file descriptor leak if bind_peer fails 2023-05-13 23:18:07 +02:00
Yves Rutschle 5666a1bb9d die if fd is not in cnx, which should be impossible (current behaviour results in illegal array dereferencing, which is worse) 2023-05-13 23:11:03 +02:00
Yves Rutschle 7b9c7f0fb2 check accept() return value 2023-05-13 23:00:26 +02:00
Yves Rutschle eec2446723 fix error messages 2023-05-13 22:57:08 +02:00
Yves Rutschle d29c9524bd fix unbalance of va_start/va_end 2023-05-13 22:51:04 +02:00
Yves Rutschle db4ae0ef9d fix potential memory leak if the second malloc fails 2023-05-13 22:40:53 +02:00
Yves Rutschle ee48dae8c5 fix potential file descriptor leak if set_nonblock () fails 2023-05-13 22:36:21 +02:00
Yves Rutschle 842f6b0473 Add mention of QUIC example (fix #376) 2023-02-19 17:24:51 +01:00
Yves Rutschle 1f64a71cde fix out-of-bounds read in sslh-ev (fix #368) 2023-01-08 22:50:56 +01:00
Yves Rutschle 00fc8e5d95 fix off-by-one error 2023-01-08 22:50:10 +01:00
Yves Rutschle b9602ab98b removed obsolete max_fd tracker 2023-01-08 22:32:17 +01:00
Yves Rutschle 486f8a0090 removed obsolete comments 2023-01-08 21:58:50 +01:00
Yves Rutschle 3a1ac6c8d7 add protocol name that was missing in previous commit 2023-01-08 17:45:50 +01:00
Yves Rutschle 9dc3e3ce56 connection loss before it is logged resulted in logging uninitialised characters 2023-01-08 17:31:05 +01:00
Yves Rűtschlé 40c616e94c downgrade TLS error to info (fix #367) 2022-11-20 18:26:20 +01:00
Yves Rűtschlé 555717e345 defensive programming in case connections get tidied while there is activity on both file descriptors (fix #355) 2022-11-09 17:48:14 +01:00
Yves Rűtschlé 02573eb44b only process write events if file descriptor has not been tidied (may fix #355) 2022-11-03 17:23:49 +01:00
Yves Rűtschlé d166b8977c document test script dependency 2022-11-03 09:32:47 +01:00
Yves Rűtschlé 4c3b52dda5 Merge branch 'master' of https://github.com/yrutschle/sslh 2022-10-29 22:31:35 +02:00
Yves Rűtschlé c981ae9853 update log format to actually catch ssh bruteforcing (fix #359) 2022-10-29 22:31:24 +02:00
Yves Rutschle 8a0de7b628 Merge pull request #362 from exussum12/patch-1
Fix typo
2022-10-21 18:08:45 +02:00
Yves Rutschle 5154630fe0 Merge pull request #365 from iamdoubz/master
Update make clean to remove ev builds
2022-10-21 18:07:11 +02:00
iamdoubz 63d5ecddca Update make clean to remove ev builds 2022-10-20 12:04:02 -05:00
Scott Dutton e412811ff1 Update config.md 2022-10-16 00:19:21 +01:00
Yves Rutschle 7e3f723699 Merge pull request #356 from utoni/fix/watcher-unitialised-value
Fixes unitialised memory access as seen in issue #355.
2022-09-22 21:10:12 +02:00
Toni Uhlig a1db2e8a92 Fixes unitialised memory access as seen in issue #355.
==1391== Conditional jump or move depends on uninitialised value(s)
==1391==    at 0x10E92F: watchers_add_read (sslh-select.c:67)
==1391==    by 0x10E92F: watchers_init (sslh-select.c:59)
==1391==    by 0x10E92F: main_loop (sslh-select.c:134)
==1391==    by 0x10DB6D: main (sslh-main.c:285)

Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2022-09-22 11:47:03 +02:00
Yves Rűtschlé 72c743e1e1 acknowledges akappner 2022-09-11 22:15:14 +02:00
Toni Uhlig 61af0b2e09 fix cppcheck complains
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2022-09-11 22:12:33 +02:00
Yves Rűtschlé 5cba44f5fa Add built-in MSRDP support 2022-09-11 21:51:06 +02:00
Yves Rutschle 9a36854ed3 Merge pull request #353 from utoni/fix/possible-format-str-exploit
fix possible format str vuln
2022-09-11 21:15:51 +02:00
Toni Uhlig b19f8a6046 fix possible format string exploit if packet dumping enabled
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2022-09-10 12:53:57 +02:00
Yves Rutschle bb685f8467 Merge pull request #344 from utoni/improve/makefile-ar
Makefile generates libsslh.a useable for other projects e.g. fuzzer.
2022-09-07 19:11:44 +02:00
Yves Rűtschlé f418ae6128 added wireguard command-line setting 2022-09-04 18:48:17 +02:00
Yves Rutschle a6df18527c Merge pull request #345 from utoni/add/wireguard
add wireguard probe
2022-09-04 18:30:09 +02:00
Yves Rűtschlé e690cb5622 Merge branch 'master' of https://github.com/yrutschle/sslh 2022-09-04 15:28:55 +02:00
Yves Rűtschlé 64c3e0ed1e improve basic.cfg 2022-09-04 15:26:39 +02:00
Toni Uhlig b971f3edcd add wireguard probe
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2022-08-26 12:27:44 +02:00
Toni Uhlig d2ec01c072 Makefile generates libsslh.a useable for other projects e.g. fuzzer.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2022-08-26 12:12:33 +02:00
Toni Uhlig c7ddee0409 fix possible buffer overflow
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2022-08-19 20:17:59 +02:00
Yves Rutschle fb8fe57bd8 Merge pull request #341 from utoni/add/teamspeak-probe
add teamspeak3 (voice only) probe
2022-08-19 18:03:12 +02:00
Yves Rutschle 79c8af6ed1 Merge pull request #340 from utoni/fix/sslh-select
changed `select(nfds, ...)` to `select(nfds + 1, ...)`, see `man 2 select`
2022-08-19 18:00:25 +02:00
Yves Rutschle c4e7261a51 Merge pull request #339 from utoni/add/config-sanity-checks
add some config sanity checks, fixes #307
2022-08-19 17:53:08 +02:00
Yves Rutschle 7e2bb7f01f Merge pull request #338 from utoni/add/openvpn-udp-probe
add openvpn udp probe
2022-08-19 17:50:39 +02:00
Toni Uhlig 4cf3749e73 add teamspeak3 (voice only) probe
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2022-08-14 10:42:58 +02:00
Toni Uhlig 8d124e1085 changed select(nfds, ...) to select(nfds + 1, ...), see man 3 select
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2022-08-13 23:40:46 +02:00
Toni Uhlig a6c5e07d69 add some config sanity checks, fixes #307
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2022-08-13 22:18:26 +02:00
Toni Uhlig aa17061e26 add openvpn udp probe
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2022-08-11 17:22:44 +02:00
Yves Rűtschlé 9d10989d55 reword documentation to account for sslh-ev 2022-07-24 17:59:11 +02:00
Yves Rűtschlé 8b604a3db7 defensive programming against inconsistent configuration (fix #336 more) 2022-07-24 17:50:13 +02:00
Yves Rűtschlé 5168fe081a add mention of sslh-ev 2022-07-21 18:24:36 +02:00
Yves Rűtschlé 7b923f793e fix packages names for Debian 11 2022-07-21 18:20:56 +02:00
yrutschle a4db163a69 config sanity check that there is at least one target protocol for each family that we listen to (fix #336) 2022-07-10 21:16:41 +02:00
yrutschle b36486bb6d prepare v2.0 2022-06-07 20:57:44 +02:00
yrutschle d16d132f0e document udp_max_connections in example.cfg 2022-05-31 22:49:39 +02:00
yrutschle 3fffce323d fix dependencies for hash.o (fix #333) 2022-05-25 17:07:19 +02:00
yrutschle 83478818df update comment to mention sslh-ev 2022-05-22 22:33:27 +02:00
yrutschle 750e828d49 reinstate checking of FD_SETSIZE for sslh-select 2022-05-22 22:32:22 +02:00
yrutschle 82aeedefcd allow gap set and get to be inlined 2022-05-05 22:40:12 +02:00
yrutschle f6fe735171 sort target protocols as TCP or UDP, so only appropriate probes are called by the listeners 2022-05-05 17:45:40 +02:00
yrutschle 78827d75fe provide simple output to stderr (fix #330) 2022-05-05 09:12:45 +02:00
yrutschle 7228c0ebc3 refactor: move TCP code to new object tcp-listener 2022-05-03 17:03:30 +02:00
yrutschle da194f15f0 make local functions static 2022-05-02 23:01:28 +02:00
yrutschle d23a537d62 regerenate c2s files 2022-04-30 09:56:08 +02:00
Yves Rutschle aa14090bcc Merge pull request #329 from utoni/add/log-to-file
Added support for logging to a file.
2022-04-30 09:53:07 +02:00
yrutschle 006706a901 typo 2022-04-30 09:38:30 +02:00
lns f9831df8bc Added support for logging to a file.
* Added ASAN/LSAN/UBSAN support via Makefile
 * Fixed a memory leak

Signed-off-by: lns <matzeton@googlemail.com>
2022-04-28 15:19:18 +02:00
yrutschle c78a50c1d7 remove obsolete code 2022-04-27 17:33:36 +02:00
yrutschle 92e8a3e256 manage timeout with lists instead of linear searches 2022-04-27 17:29:02 +02:00
yrutschle cd664574f1 manage timeout with lists instead of linear searches 2022-04-27 17:28:13 +02:00
yrutschle 449fabba51 linked list sorted by timeout times 2022-04-24 18:35:09 +02:00
yrutschle cd5d75fed9 fix off-by-one error that wont time out highest UDP file descriptor 2022-04-24 18:31:27 +02:00
yrutschle 50f5af394b remove obsolete declaration 2022-04-18 22:55:15 +02:00
yrutschle 8ac93c3e9d abstract connection activation 2022-04-18 21:25:07 +02:00
Yves Rutschle 43bd660df2 Merge pull request #328 from jerome992/master
fixed docker compilation error
2022-04-10 16:24:50 +02:00
yrutschle 35036c94c7 make UDP hash size configurable 2022-04-10 09:03:53 +02:00
yrutschle 21d00bd29d remove globals for hash size 2022-04-10 08:45:01 +02:00
Jerome 32b065d895 fixed docker compilation error 2022-04-09 22:09:40 +02:00
yrutschle cd7afaa00d fix comment to reflect floorless algorithm 2022-04-09 13:27:30 +02:00
yrutschle 53ae8bb913 remove floor entirely 2022-04-09 13:22:55 +02:00
yrutschle 97a67500ea remove need for floor at insert 2022-04-08 21:10:20 +02:00
yrutschle 33d73dd514 invert distance counting 2022-04-08 21:09:45 +02:00
yrutschle 63b503e27f simplify remove with distance computation 2022-04-08 19:55:22 +02:00
yrutschle 2e0d6b6e8e removed obsolete variable 2022-04-08 18:40:39 +02:00
yrutschle 4625883b7f simplify insertion using distance instead of floors and wrapped 2022-04-08 18:38:29 +02:00
yrutschle 07ceb99280 remove unused variable 2022-04-02 19:04:49 +02:00
yrutschle 5fa03ec9a3 fix hash item type definition 2022-04-02 18:59:32 +02:00
yrutschle 9ce9b5cd82 Integrate hash for UDP 2022-04-02 18:48:24 +02:00
yrutschle 97810cf0b2 Merge branch 'master' of https://github.com/yrutschle/sslh 2022-04-02 15:10:51 +02:00
Yves Rutschle f8684fc9d6 add resolve_on_forward to c2s files (fix #326) 2022-04-01 19:35:16 +02:00
Yves Rutschle 5def70dff5 fix dependencies so conf2struct gets called before sslh-conf.h is used 2022-04-01 19:34:25 +02:00
yrutschle 478e1fc92f abstract hash indexes 2022-03-31 22:08:56 +02:00
Yves Rutschle 96b1aa86d8 Merge pull request #325 from g1franc/patch-1
Update README.MacOSX
2022-03-31 15:27:00 +02:00
g1franc b1517f27a8 Update README.MacOSX
Should be *tls* as argument here instead of old *ssl*
2022-03-31 00:06:48 +02:00
yrutschle 454a261c95 initial hash object with testing framework 2022-03-30 22:56:41 +02:00
yrutschle 9c3274359a document resolve_on_forward 2022-03-23 21:33:45 +01:00
Yves Rutschle a71de786d6 Merge pull request #323 from milkpirate/feat/resolve-on-connect
Feat/resolve on connect
2022-03-23 21:26:25 +01:00
Yves Rutschle dd167d13a3 Merge pull request #324 from beango1/patch-1
typo correction
2022-03-23 21:23:05 +01:00
beango1 759e68c8d2 typo correction 2022-03-23 10:22:44 -04:00
Paul Schroeder 78bc954769 review
Signed-off-by: Paul Schroeder <milkpirate@users.noreply.github.com>
2022-03-19 23:18:29 +01:00
Paul Schroeder 3f5c81d2f6 be more clearly
Signed-off-by: Paul Schroeder <milkpirate@users.noreply.github.com>
2022-03-18 18:03:58 +01:00
yrutschle ff810d41b0 Merge branch 'master' of https://github.com/yrutschle/sslh 2022-03-18 18:03:29 +01:00
yrutschle d922086f53 handle accept failure properly (fix #322) 2022-03-18 18:03:18 +01:00
yrutschle 1e0578c082 don't log to syslog when testing 2022-03-18 18:02:32 +01:00
Paul Schroeder 87577ae5f6 add functionality
Signed-off-by: Paul Schroeder <milkpirate@users.noreply.github.com>
2022-03-18 17:59:54 +01:00
Paul Schroeder 875fa488c9 add option and description
Signed-off-by: Paul Schroeder <milkpirate@users.noreply.github.com>
2022-03-18 17:59:29 +01:00
yrutschle 91d148f66c removed obsolete resolve_name function 2022-03-17 21:51:54 +01:00
yrutschle 66f4b18121 ensure conf2struct is called early in the make 2022-03-17 21:51:01 +01:00
Yves Rutschle bccb78e1a3 Merge pull request #317 from jeffre/master
docs: corrections to docker usage
2022-01-23 16:26:55 +01:00
jeffre 6f497b6c8b docs: corrections to docker usage 2022-01-20 20:21:12 -07:00
yrutschle 4e6145576e warn systemd that sslh will fork (fix #314) 2021-12-23 21:56:31 +01:00
yrutschle 58ed185316 missing header dependency 2021-12-19 19:30:21 +01:00
yrutschle 122649ce3c fix log level for unknown ALPN (fix #313) 2021-12-12 09:30:07 +01:00
yrutschle 31b096a0f4 Merge branch 'master' of https://github.com/yrutschle/sslh 2021-12-09 21:33:37 +01:00
yrutschle adaf407b8a added sslh-ev to install docs 2021-12-09 21:29:48 +01:00
Yves Rutschle 4e72800f2e Merge pull request #311 from Belisarith/master
Correct wrong version of pcre in Dockerfile
2021-11-25 11:02:40 +01:00
Belisarith 18eeaa579a additional fix dockerfile, otherwise docker is not runnable 2021-11-23 12:57:16 +00:00
Belisarith 80f2d758a4 Correct wrong version of pcre in Dockerfile 2021-11-23 12:40:17 +00:00
yrutschle 9798bdcaa0 Merge branch 'master' of https://github.com/yrutschle/sslh 2021-11-20 17:00:34 +01:00
yrutschle 93df6975ed set appropriate log level for SNI matching 2021-11-20 17:00:24 +01:00
Yves Rutschle 44a78bc515 document dependency on libev 2021-11-14 18:58:59 +00:00
yrutschle 711c11c820 New sslh-ev: this is functionaly equivalent to sslh-select (mono-process, only forks for specified protocols), but based on libev, which should make it scalable to large numbers of connections. 2021-11-12 09:05:18 +01:00
yrutschle ae117097ea sslh-ev in ChangeLog 2021-11-12 09:03:30 +01:00
yrutschle 0a23ca133e finalise UDP support for sslh-ev 2021-11-11 21:16:37 +01:00
yrutschle 207d482189 refactor: move udp timeout management to udp module 2021-11-09 18:27:52 +01:00
yrutschle d4d9dbb8e7 remove dependancy to watcher type in UDP timeout 2021-11-09 18:12:02 +01:00
yrutschle 8ddff5e388 sslh-ev working for TCP 2021-11-07 16:13:44 +01:00
yrutschle a80e2ceb27 remove obsolete declaration 2021-11-07 13:13:41 +01:00
yrutschle e28fa91b0f reap children properly 2021-11-07 13:13:05 +01:00
yrutschle 17313100b5 print cnx type when dumping it 2021-11-01 18:48:26 +01:00
yrutschle 4cd3ab8958 moved watcher add to watcher init 2021-10-28 15:41:09 +02:00
yrutschle 5ec1f7eb98 added missing header 2021-10-28 15:40:40 +02:00
Yves Rutschle 5b21375087 Merge pull request #310 from bket/fix_tests
Fix (some) failing tests
2021-10-28 15:07:51 +02:00
Björn Ketelaars d57a155bf4 Fix (some) failing tests
Found a couple of failing tests on Alpine Linux and OpenBSD. For the
tests to even run `ip4-localhost` has to be changed to an IP-address
(127.0.0.1). `ip4-localhost` is typically not part of `/etc/hosts`.

Output failing tests:

```
not ok 5
udp: 0
prefix: tls:
listen [1]:
    host: localhost
    port: 9025
flushing deferred data to fd 9
selecting... max_fd=11 num_probing=0
activity on fd8
closing fd 8
closing fd 9
selecting... max_fd=11 num_probing=0
#   Failed test at ./t line 59.
#          got: '1'
#     expected: 'sslh-select: Connect and write nothing'
```

```
not ok 22 - sslh-select:ssh: probe connected correctly
#   Failed test 'sslh-select:ssh: probe connected correctly'
#   at ./t line 59.
#          got: 'regex'
#     expected: 'ssh'
```

```
not ok 68 - sslh-select:ssh: probe connected correctly
#   Failed test 'sslh-select:ssh: probe connected correctly'
#   at ./t line 59.
#          got: 'regex'
#     expected: 'ssh'
```

Diff works around issues causing tests 22 and 68 to fail. As of yet, no
workaround for test 5 has been found.
2021-10-27 19:52:06 +02:00
yrutschle ec033efbbc refactor more code from sslh-select to processes.c 2021-10-26 21:45:44 +02:00
yrutschle 3389000ff3 move action processing to its own file 2021-10-17 16:02:49 +02:00
yrutschle 25abd765cb refactor: abstract watchers from loop data 2021-10-11 22:40:46 +02:00
yrutschle 2cdd60dd18 make systemd-sslh-generator work again (fix #308 again) 2021-10-04 21:34:22 +02:00
yrutschle b0aeeff465 Include log header before defining macros that depend on log levels. (fix #308) 2021-10-04 09:11:41 +02:00
yrutschle c9eff6e38d removed obsolete declarations 2021-10-03 17:25:31 +02:00
yrutschle 0cde3d794a check return values (fix #61) 2021-10-02 21:27:31 +02:00
yrutschle ed48d3964f removed obsolete prototye 2021-10-02 21:23:39 +02:00
yrutschle 7b0d486d3d removed obsolete check to verbose 2021-10-02 21:23:17 +02:00
yrutschle 0e118a109c Overhaul of the logging system: logs all have classes, and
each class can be configured independently to write to
stderr or syslog.
2021-10-02 15:40:32 +02:00
yrutschle 9955cc6560 describe verbose options 2021-10-02 15:38:22 +02:00
yrutschle caa62875c1 remove --verbose option 2021-09-27 13:28:21 +02:00
yrutschle c8fce0a02f make sure no error will go to stderr if in inetd (fix #303) 2021-09-27 13:21:16 +02:00
yrutschle 4277d27063 migrate last messages to new log system 2021-09-27 13:16:30 +02:00
yrutschle 16bf1a6aca make echosrv independant from common macros 2021-09-27 13:01:20 +02:00
yrutschle 4f0f5017bc remove obsolete prototype 2021-09-27 12:55:57 +02:00
yrutschle 70b31a48d9 migrate generic system call failure checks to new log system 2021-09-27 12:53:41 +02:00
yrutschle 66caf8a31b remove log_message 2021-09-27 12:51:37 +02:00
yrutschle 4d3cc9c925 migrate some more common.c to new log system 2021-09-27 12:46:51 +02:00
yrutschle 2e11001087 migrate UDP to new log system 2021-09-27 12:43:03 +02:00
yrutschle 6ea7d48f86 migrate tls.c and probe.c to new log system 2021-09-26 16:55:31 +02:00
yrutschle 3fb1201b3f merged transparent proxy setups together, clarifying what is not known to work 2021-09-26 16:28:06 +02:00
yrutschle e9e7ada069 convert to hash-based titles 2021-09-26 16:13:23 +02:00
yrutschle e6cbbe9511 migrate common.c to new logging system 2021-09-26 15:53:21 +02:00
yrutschle e5f16b93ce hexdump writes to parametrable msg_info 2021-09-19 21:54:47 +02:00
yrutschle 673c40954e migrate sslh-fork to new log system 2021-09-19 20:29:43 +02:00
yrutschle f7b6f669a4 sslh-select to use new log system 2021-09-19 20:24:46 +02:00
yrutschle dbad46a358 remove obsolete debug code 2021-09-19 15:19:37 +02:00
yrutschle 098a55fd1d new logging system: now with message classes 2021-09-19 15:14:38 +02:00
yrutschle 5e27806545 new logging system: now with message classes 2021-09-19 15:13:04 +02:00
yrutschle 317c08604b move logging code to its own file 2021-09-15 21:51:11 +02:00
yrutschle 3013658b20 test to drop connection before writing anything (fix #285) 2021-08-28 16:33:20 +02:00
yrutschle a704c7f7f5 fix #302 2021-08-28 16:03:58 +02:00
yrutschle 1a3341c2a4 be more defensive when allocating and extending gap 2021-08-24 20:07:28 +02:00
yrutschle 4a6bbda60d remove obsolete usage string and added lost version option 2021-08-24 14:10:14 +02:00
yrutschle fa848f2ae9 do not timeout TCP connections (fix #300) 2021-08-24 13:38:18 +02:00
Aaron Madlon-Kay a3640775bb Allow supplying additional CFLAGS
Package managers may want to supply default CFLAGS that should be used *in addition* to project-specific ones.
2021-08-18 08:49:33 +09:00
Yves Rutschle 6aa19d080a updated version numbers for release 2021-08-17 21:55:18 +02:00
yrutschle a43385b8db remove hardcoded quick50 and teamspeak. to be replaced with a generalised regex setup to come in next version 2021-08-13 18:03:59 +02:00
yrutschle 1261a5f4c7 updated some references to obsolete ssl option 2021-08-06 22:43:38 +02:00
yrutschle 40da147efd keep track of next UDP timeout to only go through all connections when that happens 2021-08-06 22:29:00 +02:00
yrutschle e4936454c5 refactor: take time only once when computing UPD timeouts 2021-08-05 17:05:08 +02:00
yrutschle 63f9c4a582 added syslog probe (fixes #34) 2021-08-04 15:29:05 +02:00
yrutschle 0e45107797 advertise new UDP protocols 2021-08-01 22:02:42 +02:00
yrutschle 4584e719e1 added hardcoded probe for Teamspeak3 2021-08-01 22:01:59 +02:00
yrutschle 75e426c80f added hardcoded probe for QUICK50 2021-08-01 21:35:24 +02:00
yrutschle e40d43bea5 removed obsolete ssl test pattern 2021-08-01 21:27:04 +02:00
yrutschle 2196146224 Merge branch 'master' of https://github.com/yrutschle/sslh 2021-08-01 21:25:39 +02:00
Yves Rutschle 2f822741b3 Merge pull request #297 from jeremiejig/master
update doc CAP_NET_RAW, remove CAP_SETUID/SETGID
2021-08-01 21:25:05 +02:00
Jeremiejig 760def3444 update doc CAP_NET_RAW, remove CAP_SETUID/SETGID
Update documentation CAP_NET_ADMIN -> CAP_NET_RAW.

Remove useless capabilities CAP_SETUID CAP_SETGID in service unit files.
There are only useful if not using either User=sslh nor DynamicUser=true
2021-08-01 21:08:52 +04:00
yrutschle 4b885b4a2c remove obsolete variables 2021-07-31 23:34:43 +02:00
yrutschle ce23f202b7 use pcre2 api directly 2021-07-31 23:12:55 +02:00
Yves Rutschle 82b8ba547e Merge pull request #205 from inztar/master
add Dockerfile
2021-07-31 16:28:42 +02:00
Yves Rutschle 8e38d56167 Merge pull request #294 from wltu/fix-unused-result
Fixed unused result issue with write()
2021-07-31 16:20:06 +02:00
Yves Rutschle ea3b6794e1 Merge pull request #296 from lnslbrty/improved/example-config-udp
Added some example regex for UDP forward.
2021-07-31 16:16:15 +02:00
Toni Uhlig e42f165ef5 Added some example regex for UDP forward.
Signed-off-by: Toni Uhlig <matzeton@googlemail.com>
2021-07-30 23:00:24 +02:00
yrutschle 37d2756703 move to PCRE for conf file 2021-07-23 22:36:20 +02:00
Willy Tu ec25ab56e8 Fixed unused result issue with write()
Simply print an error message if the write result is < 0.
2021-07-23 13:21:14 -07:00
yrutschle 7fb65ad0ac refactor: remove sideeffects from udp_timedout 2021-07-18 21:50:40 +02:00
yrutschle c43882c85f refactor: move timeout management to separate function 2021-07-18 21:36:46 +02:00
yrutschle 4a4c571116 document UDP support 2021-07-16 22:38:53 +02:00
yrutschle c049885758 document UDP support 2021-07-16 22:37:41 +02:00
yrutschle de0ec959d9 make udp timeout configurable per protocol 2021-07-08 14:13:04 +02:00
yrutschle e7df8eeaa1 removed static known_source arrays, UDP connections are now managed dynamically 2021-07-07 19:58:08 +02:00
yrutschle 8af87ebbad report errors as E instead of M, and consolidate restarts as simplified roman numerals 2021-07-07 19:56:17 +02:00
yrutschle bf57d63c3a fix message for fd closure on timeout 2021-07-02 08:22:09 +02:00
yrutschle 862e33cfec moved UDP support from sslh-fork to sslh-select 2021-07-01 22:44:35 +02:00
yrutschle 24e7f46a43 fix for libconfig 1.7.3 (#292) 2021-06-23 11:48:59 +02:00
yrutschle bf2053eb79 fail gracefully if libconfig fails to find root element 2021-06-22 21:34:08 +02:00
yrutschle 1ad450a444 sslh-select sets O_NONBLOCK *before* calling connect, which prevents hanging on an unresposive server (fix #258) 2021-05-28 13:38:45 +02:00
yrutschle 300e1916c3 add recv and sendto addresses 2021-05-16 15:08:33 +02:00
yrutschle e678428334 clarify function name 2021-05-15 17:57:33 +02:00
yrutschle edc42ca13b clarify variable name 2021-05-15 17:39:34 +02:00
yrutschle c41ec489e0 changed CAP_NET_ADMIN to CAP_NET_RAW as it's enough 2021-05-11 06:44:27 +02:00
yrutschle 79f49f4481 refactor t_load to use test.cfg as base 2021-05-09 16:40:38 +02:00
yrutschle 8e130882fc sslh-fork drops all capabilities after connecting to server side 2021-05-09 15:46:25 +02:00
yrutschle 030ef64b99 refactor: purified set_capabilities, told to keep or drop CAP_NET_ADMIN instead of deciding for itself 2021-05-09 15:39:14 +02:00
yrutschle cf4f4cbebe typo 2021-05-08 22:33:45 +02:00
yrutschle 905ac95ca1 do not drop CAP_NET_ADMIN if any of the protocols require transparent proxying 2021-05-08 07:44:39 +02:00
yrutschle da3b51e056 upgraded argtable3 to current last version to fix a parsing issue with command line options 2021-05-08 07:37:20 +02:00
yrutschle 308b54aa8a remove caveat about UDP refactor 2021-05-08 07:06:14 +02:00
yrutschle 987643878f when verbose, precise which listen sockets are udp 2021-05-01 16:15:34 +02:00
yrutschle 46e741e9c2 add UDP support to echosrv 2021-04-25 09:06:31 +02:00
yrutschle c3d019284d made echosrv independant from common.o and with its own configuration 2021-04-24 10:31:41 +02:00
yrutschle 4c570e8d57 remove some debug messages 2021-04-21 08:48:31 +02:00
yrutschle 0a00220b42 justify use of select 2021-04-21 08:43:21 +02:00
yrutschle a0f9fc164e changelog the sslh-select refactoring 2021-04-21 08:42:27 +02:00
yrutschle 517e4ad5b4 Merge branch 'master' of https://github.com/yrutschle/sslh 2021-04-19 09:39:05 +02:00
yrutschle b258b0e0f7 manage probing sockets in a specific list instead of searching through all connections 2021-04-19 09:38:22 +02:00
yrutschle 02f6c6999d fix client/server protocol to indicate process death, and make process mortal again 2021-04-18 11:18:26 +02:00
yrutschle 41a914b350 alloc_cnx returns cnx pointer instead of simple error code 2021-04-17 21:29:38 +02:00
yrutschle a487ad4cbe fix ssh test and add random delays to exercise probing code 2021-04-17 21:28:55 +02:00
yrutschle 21ed247acb removed obsolete function 2021-04-17 21:00:52 +02:00
yrutschle dfa5d3cedb simplified some redundant parameters 2021-04-17 17:53:45 +02:00
yrutschle 74ce2acdc5 make t_load pretty print 2021-04-16 22:04:45 +02:00
yrutschle 0e3242c115 fix to work with test.cfg 2021-04-14 23:27:44 +02:00
yrutschle 5715a9f0bd stop managing connections as array; instead, allocate connections, and fd2cnx is the only pointer to it 2021-04-14 23:26:33 +02:00
yrutschle de474e1d07 stop managing connections as array; instead, allocate connections, and fd2cnx is the only pointer to it 2021-04-14 23:26:01 +02:00
yrutschle cb199ed3c6 remove redundant dependency 2021-04-11 09:38:36 +02:00
yrutschle 7485afe397 refactor accept to its own function 2021-04-10 08:26:22 +02:00
yrutschle 417722e3c1 remove obsolete definitions 2021-04-03 21:40:53 +02:00
yrutschle 9fca586735 remove over-verboseness 2021-04-03 21:39:43 +02:00
yrutschle 0613d412a2 refactor: write activity checked on select output rather than on each connection object 2021-04-03 21:34:47 +02:00
yrutschle b2ee07655a typo 2021-04-03 21:27:44 +02:00
yrutschle 465ecdda5b fix busy loop bug 2021-04-03 21:27:35 +02:00
yrutschle 3edf6a9d22 refactor: move write process to its own function 2021-04-02 22:20:04 +02:00
yrutschle 7a88a50c32 refactor: remove redundant paramters 2021-04-02 16:02:23 +02:00
yrutschle e7a5bbc4e1 removed some over-verboseness 2021-03-30 20:16:38 +02:00
yrutschle 40557c58ad working version with read activity checked on select output rather than on each connection object 2021-03-29 21:56:21 +02:00
yrutschle f0e1aaf82c refurbished valgrind launcher 2021-03-29 21:54:54 +02:00
yrutschle f51781664c add server-side file descriptor to collection 2021-03-28 12:22:36 +02:00
yrutschle 5948270b95 update API 2021-03-28 11:47:33 +02:00
yrutschle 758be2a8ab clarify API: allocate cnx in collection from a new fd, or add a fd to an existing connection in a collection 2021-03-28 11:40:40 +02:00
yrutschle 654ff0b5b0 rebuild when headers change 2021-03-28 11:39:49 +02:00
yrutschle 15a5073559 keep track of which connection contains what file descriptor 2021-03-23 22:58:10 +01:00
yrutschle c2b6bf246c refactor: make collection part of select object 2021-03-23 21:43:07 +01:00
yrutschle 6366d50b89 refactor: use fd_info instead of passing fd_sets directly 2021-03-23 13:59:15 +01:00
yrutschle 5b93e4ab55 abstracted collection management to its own file 2021-03-21 16:42:37 +01:00
yrutschle 1c63b06cc8 refactor: move collection handling towards a foreach construct 2021-03-21 14:01:12 +01:00
yrutschle 71c617050d fix collection extension 2021-03-17 21:16:08 +01:00
yrutschle 0409775a1a fix t_load so it works again! 2021-03-17 18:48:27 +01:00
Yves Rutschle b72baa0622 Merge pull request #280 from ffontaine/master
Makefile: fix static linking with pcre
2021-03-16 20:32:46 +01:00
yrutschle 790d639ad0 echosrv to prefix only once 2021-03-07 17:43:42 +01:00
yrutschle e884fc616d add conditional inclusion for header 2021-03-03 18:20:45 +01:00
yrutschle 7e63dedca3 refactor: abstract connection list management to a new 'collection' type 2021-03-03 14:38:24 +01:00
yrutschle 49d4080afd refactor pre-increment to more idiomatic post-increment (fix #282) 2021-03-03 14:29:25 +01:00
yrutschle be4e126f3a refactor: move all read process code to its own function 2021-02-28 18:19:18 +01:00
yrutschle 34bcc2bbc8 refactor: move probing actions to its own function 2021-02-28 10:46:17 +01:00
yrutschle 90975fd6c3 refactor: move all file descriptor info for select to its own struct 2021-02-28 10:12:13 +01:00
yrutschle f91f16d753 make local functions static 2021-02-27 15:33:41 +01:00
yrutschle dae8101a50 remove redundant macro definition 2021-02-27 15:20:15 +01:00
yrutschle 68fc10aed0 comment udp_listener prototype 2021-02-23 21:19:23 +01:00
yrutschle 49c136691c refactor: move UDP code to its own file 2021-02-23 21:17:39 +01:00
yrutschle adb27aa4a3 add a per-protocol transparent proxy option 2021-02-21 21:03:01 +01:00
yrutschle 9ff9723278 precised size types to ssize_t instead of int 2021-02-08 22:50:53 +01:00
yrutschle 52a9356c35 fix typo 2021-01-09 15:34:09 +01:00
yrutschle ac3b43d4bd document choice for UDP buffer management 2021-01-09 15:24:31 +01:00
yrutschle 368f286ce5 added FAQ for virtual hosting 2020-12-20 15:13:13 +01:00
yrutschle 05a835ff1f begin of release not for UDP 2020-12-06 15:50:08 +01:00
yrutschle 0a3d5874b4 free udp connections after some timeout 2020-12-05 22:59:38 +01:00
yrutschle a42d4f8049 forward server responses back to sender 2020-12-04 16:31:52 +01:00
yrutschle 1e33455fe7 don't try to probe a connection that's finished (should fix #284) 2020-12-02 22:49:19 +01:00
yrutschle a3d9df87cb fix typo and explicit a test 2020-12-02 22:48:17 +01:00
yrutschle 247d60d8e6 dump deferred data when dying from unexpected fd activity 2020-12-02 22:05:09 +01:00
yrutschle 7d820049a2 preliminary udp support: forwards incoming packets, no return yet 2020-11-29 15:51:04 +01:00
yrutschle 09597bfa42 refactor: make probe_buffer hexdump input 2020-11-29 10:42:58 +01:00
yrutschle 41e3b0cd1d warn about refactor 2020-11-29 10:19:50 +01:00
yrutschle 76d0351452 change listener_pid type to pid_t 2020-11-14 18:31:43 +01:00
yrutschle fccaa5fa9f refactor: separate probe reading from socket from probe on buffer, so we can call probe on buffer independantly 2020-11-11 22:05:45 +01:00
yrutschle f077101835 refactor: move tcp listener to its own function 2020-11-08 19:09:18 +01:00
yrutschle f3230b4a94 abstract listening sockets so we have protocol information alongside the socket -- echosrv and sslh-select 2020-11-07 22:44:37 +01:00
yrutschle c12f7a1ade abstract listening sockets so we have protocol information alongside the socket 2020-11-07 22:31:49 +01:00
yrutschle ebeabb6c18 add is_udp option and listen to UDP ports 2020-11-07 21:29:09 +01:00
yrutschle 8a1dae8c22 refactor: move local address resolution into start_listen_socket 2020-11-07 18:55:04 +01:00
yrutschle 07719f55b8 refactor echosrv to use conf2struct 2020-11-07 09:29:26 +01:00
yrutschle 6c94bf71b8 refactor: move code to listen to a single address out of start_listen_sockets 2020-11-01 21:21:26 +01:00
Fabrice Fontaine 0d9e004d32 Makefile: fix static linking with pcre
Static build with pcre is broken since version 1.19b and
https://github.com/yrutschle/sslh/commit/cb90cc97ae64a445242e517847c6e44b7003eda4
because -lpcre has been replaced by -lpcreposix which will result in
the following static build failure:

/srv/storage/autobuild/run/instance-1/output-1/host/bin/mipsel-linux-gcc -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64  -Os   -static -static -o echosrv echosrv.o probe.o common.o tls.o  -lpcreposix -lconfig -lcap
/srv/storage/autobuild/run/instance-1/output-1/host/opt/ext-toolchain/bin/../lib/gcc/mipsel-buildroot-linux-uclibc/8.3.0/../../../../mipsel-buildroot-linux-uclibc/bin/ld: /srv/storage/autobuild/run/instance-1/output-1/host/mipsel-buildroot-linux-uclibc/sysroot/usr/lib/libpcreposix.a(libpcreposix_la-pcreposix.o): in function `regfree':
pcreposix.c:(.text+0x120): undefined reference to `pcre_free'

So append -lpcre after -lpcreposix

Fixes:
 - http://autobuild.buildroot.org/results/a601824fc0c205a6a940e0f9f079ce2c39840605

Signed-off-by: Fabrice Fontaine <fontaine.fabrice@gmail.com>
2020-10-15 08:05:02 +02:00
yrutschle 0c388cb367 update mailing list address 2020-09-23 21:34:14 +02:00
yrutschle 9f99f296b1 warn about unknown settings in the configuration file 2020-08-29 18:22:42 +02:00
yrutschle 8b6e06e6c7 support for libconfig 1.4.9. Fix #275 2020-08-11 17:37:23 +02:00
yrutschle 5cdff8c558 document how to provide useful information in case of segfault 2020-08-04 17:27:09 +02:00
yrutschle 72438dc1ae dont try to use conffile count if libconfig is not present, fix #274 2020-08-04 16:37:49 +02:00
yrutschle e67d6ff905 removed obsolete support for . Use instead 2020-07-30 09:45:50 +02:00
yrutschle 0af37a1bed initialise runtime data, which avoids using bad pointers in tls.c as in issue 273 2020-07-29 22:52:48 +02:00
yrutschle 08ce89b6e2 fix tinc configuration target 2020-07-29 22:31:02 +02:00
yrutschle 99d4a6d4a2 explicit obsoletion of ssl param 2020-07-29 22:23:36 +02:00
yrutschle 2c93a015ea make libconfig optionnal again 2020-07-24 16:51:06 +02:00
yrutschle 3315a727dc update required conf2struct version 2020-07-19 21:05:36 +02:00
yrutschle 8638199f13 test for command line parameters 2020-07-19 18:32:57 +02:00
yrutschle d7cf82424b add --anyprot command line option (fix #272) 2020-07-18 17:27:32 +02:00
yrutschle ac8563525e fix Changelog for -v 2020-07-18 17:25:39 +02:00
yrutschle 2f983625d0 documentation update 2020-07-18 16:54:23 +02:00
yrutschle f6b11a424b reset config file option to --config 2020-07-17 21:58:23 +02:00
yrutschle cf46cd5bb4 Merge pull request #271 from bket/config_init
Always initialize config_t structure, fixes #270
2020-07-15 21:15:49 +02:00
Björn Ketelaars b473622698 Always initialize config_t structure, fixes #270
Without this change the config_t structure is ONLY initialized when it
meets a specific criterion. If this criterion is not met the config_t
structure is still used, which can cause a segmentation fault.

Fixes #270.
2020-07-14 06:57:43 +02:00
Yves Rutschle de8e5725c2 automatically sign release 2020-07-11 21:39:37 +02:00
Yves Rutschle 0cc516bf51 v1.21 2020-07-11 21:30:26 +02:00
yrutschle 337bd34fb7 fix robustness tests 2020-07-10 23:27:07 +02:00
yrutschle 46d9796bd6 fail as soon as a listen address cannot be resolved 2020-07-10 23:12:03 +02:00
yrutschle 219163ac27 dont keep going if config parse failed 2020-07-10 22:49:41 +02:00
yrutschle 8ddbe59883 warn that transparent proxy is hard 2020-07-06 22:25:42 +02:00
yrutschle c5b0932bad syslog_facility 'none' disables syslog 2020-06-19 23:39:25 +02:00
yrutschle 9e2cc93973 syslog connections when specifying probes on the command line 2020-06-19 23:38:33 +02:00
yrutschle 408c5741f8 rewind logged messages so it does not crash... 2020-06-19 23:01:25 +02:00
Yves Rutschle c595aadb4d log to syslog even if in foreground 2020-05-28 10:13:54 +00:00
yrutschle 61191edf7c initialised FAQ 2020-04-10 21:45:04 +02:00
yrutschle 1009f556fc initialised FAQ 2020-04-10 21:43:46 +02:00
yrutschle 98807771e0 clarify error message for setsockopt IP_TRANSPARENT 2020-01-26 21:35:09 +01:00
yrutschle 4610e220bd update c2s version requirement 2020-01-26 21:20:48 +01:00
yrutschle dfa764e2e8 config from new c2s: underscore and dashes are equivalent 2020-01-26 21:19:16 +01:00
yrutschle 10fe9c6e27 Merge branch 'master' of https://github.com/yrutschle/sslh 2020-01-26 21:13:04 +01:00
yrutschle d1f782e1fe Merge pull request #255 from jamesmacwhite/cfg-example-tls
Update SSL example to TLS
2020-01-02 21:12:18 +01:00
James White 9ed44ee09b Update SSL example to TLS
Using will throw a deprecation warning message
2020-01-02 09:24:32 +00:00
yrutschle e913eeeb88 explicit what sslhconf.cfg is for 2019-12-30 21:06:09 +01:00
yrutschle 9d1d9f850a added config parser generated by conf2struct, so sslh builds without conf2struct after cloning 2019-12-30 20:44:16 +01:00
yrutschle 588883eb42 Fix on-timeout setting so it is no longer ignored (issue #253) 2019-12-27 18:26:16 +01:00
yrutschle bae16ab29e Merge branch 'master' of https://github.com/yrutschle/sslh 2019-11-15 22:20:48 +01:00
yrutschle 32851d2041 minor doc clarification for dependencies 2019-11-15 22:20:31 +01:00
yrutschle 49e8bff01b Merge pull request #248 from muellerj/master
Fix warnings
2019-11-14 23:16:24 +01:00
Jonas Mueller 87aaa156e0 Add explicit casts to remove warnings
for incompatible-pointer-types-discards-qualifiers
2019-11-05 20:28:58 +01:00
Jonas Mueller ef8233a839 Fix warnings for format-string-security 2019-11-05 20:11:44 +01:00
Jonas Mueller 125458df51 Fix warnings for return-type 2019-11-05 20:06:23 +01:00
yrutschle d2cfa3c603 Merge pull request #242 from ideal/master
Fix remaining available size of buffer
2019-09-08 20:56:25 +02:00
yrutschle 891bcf9966 fix error message upon regex syntax error 2019-09-06 21:05:46 +02:00
yrutschle 544c2b6d2f Merge pull request #240 from niobos/feature/descriptive-ps-name
Change process name to indicate task of process
2019-09-05 21:49:32 +02:00
Niobos 0380a4309f Change process name to indicate task of process 2019-09-05 16:40:59 +02:00
yrutschle 3aa245efa5 separate connection information creationg from log emission 2019-09-05 15:48:33 +02:00
ideal bc28d6ce19 the remaining size of buffer should minus prefix_len 2019-09-04 23:02:13 +08:00
yrutschle d78c810d84 specify conf2struct version 2019-08-30 22:25:38 +02:00
yrutschle ed3cd40e23 adapt config format to conf2struct v1.0 2019-08-29 22:21:00 +02:00
yrutschle 12edc3dbca merged master 2019-08-16 10:06:31 +02:00
yrutschle 0931dfdf0b specify c2s commit to be used for building 2019-08-16 08:13:34 +02:00
yrutschle 0d5a2f6922 Merge pull request #236 from Jason-Cooke/patch-1
docs: fix typo
2019-07-09 21:50:49 +02:00
Jason Cooke 49e59734cb docs: fix typo 2019-07-09 09:12:39 +12:00
Yves Rutschle 914dc98fb3 added transparent proxy figure 2019-07-08 20:18:57 +00:00
yrutschle 1dc0088b74 Merge pull request #235 from FlexMcMurphy/patch-3
Delete tproxy.svg
2019-07-08 22:12:31 +02:00
FlexMcMurphy 9416617456 Delete tproxy.svg
Delete this svg file and replace with fixed svg diagram with text boxes displaying correctly.
2019-07-05 23:52:24 +01:00
yrutschle 7e047e35b3 updated architecture figure to SVG 2019-07-03 22:45:04 +02:00
yrutschle 8ffcf5ab03 Merge pull request #233 from FlexMcMurphy/patch-1
Delete tproxy.jpg
2019-07-03 22:26:59 +02:00
yrutschle a917149e14 Merge pull request #234 from FlexMcMurphy/patch-2
Update tproxy.md
2019-07-03 22:26:43 +02:00
FlexMcMurphy 08a64e99b4 Update tproxy.md
Minor formatting changes. Could also update tproxy.jpg to the svg version.
2019-06-30 12:28:53 +01:00
FlexMcMurphy 0ef57f1500 Delete tproxy.jpg 2019-06-30 11:47:42 +01:00
yrutschle fde86a778d remove contents from README that was added in doc/ 2019-06-29 21:51:02 +02:00
yrutschle 5bd0235d38 split documentation into separate files in doc/ 2019-06-29 21:50:05 +02:00
yrutschle 177d337ac9 added new tproxy doc 2019-06-29 19:23:56 +02:00
yrutschle 8e203c897e added SOCKS5 to README 2019-06-24 21:39:18 +02:00
yrutschle 6e9f24153f Merge pull request #232 from jmccrohan/master
Update Let's Encrypt entry in example.cfg
2019-06-17 20:57:06 +02:00
Jonathan McCrohan f029b4098b Update Let's Encrypt entry in example.cfg
Update Let's Encrypt entry in example.cfg for tls-alpn-01 challenge.
Previous entry was based on tls-sni-01 challenge which is deprecated and
was disabled in March 2019.

Signed-off-by: Jonathan McCrohan <jmccrohan@gmail.com>
2019-06-16 23:26:33 +01:00
yrutschle c1369910c9 Merge pull request #229 from rugbylug/evening_of_docs
Fix a typo
2019-05-20 21:28:53 +02:00
Ondřej Kuzník d756f7d504 Fix a typo 2019-05-16 20:52:04 +01:00
yrutschle 6d99f780ef Merge branch 'master' of https://github.com/yrutschle/sslh 2019-05-13 15:21:36 +02:00
yrutschle d5baed3f18 Fix fd2fd return value which should not be -1 on error 2019-05-13 15:21:22 +02:00
yrutschle 5684959dd6 Merge pull request #225 from ffontaine/master
fix version extraction when building in a larger git tree
2019-04-06 22:12:30 +02:00
Taras Inzyk 95a6577cda add Dockerfile 2019-04-06 17:26:08 +05:00
Fabrice Fontaine b5d8b2d199 fix version extraction when building in a larger git tree
sslh uses host git to extract its own version number. In buildroot, this
is an issue since extracted information is conflicting with buildroot git
status if we use git as VCS for buildroot.

Since these git calls are legitimate only if git is used for the sslh
subtree only, this patch adds a check : a .git directory has to exist at
the root of the project to enable git-extracted version string.

Signed-off-by: David Bachelart <david.bachelart@bbright.com>
[yann.morin.1998@free.fr: fix troll character U+c2a0]
Signed-off-by: "Yann E. MORIN" <yann.morin.1998@free.fr>
[Retrieved from:
https://git.buildroot.net/buildroot/tree/package/sslh/0001-secure-version-while-building-sslh-in-a-larger-git-t.patch]
Signed-off-by: Fabrice Fontaine <fontaine.fabrice@gmail.com>
2019-04-05 23:05:25 +02:00
yrutschle e528f519bc relax address matching rule so it matches more than just 'localhost' 2019-03-11 22:21:56 +01:00
yrutschle b529069029 print a synthetic test report 2019-03-11 21:31:24 +01:00
yrutschle 67eb471c6f Merge branch 'tfo' 2019-03-10 10:12:33 +01:00
yrutschle 2705face30 TCP_FASTOPEN changelog 2019-03-10 10:11:28 +01:00
yrutschle 4e725e1520 added TFO for listening socket 2019-03-10 10:11:06 +01:00
yrutschle b0c3c8fdbc manage TFO already done in connect call 2019-03-10 09:53:52 +01:00
yrutschle 15f733e572 add tfo_ok configuration setting 2019-03-10 09:46:06 +01:00
yrutschle ff91f94315 Merge client TFO setting 2019-03-10 09:39:48 +01:00
Craig Andrews 0a880ea607 Use TCP Fast Open for client sockets
Set the TCP_FASTOPEN_CONNECT option on client sockets to signal desire to use TCP Fast Open.

See https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=19f6d3f3c8422d65b5e3d2162e30ef07c6e21ea2
2019-03-09 21:18:36 -05:00
yrutschle e0312b4a9d make ssh fork when on command line as used to be the case before 2019-03-09 12:36:57 +01:00
yrutschle 5a213c9650 c2s fix: initialise root config structure even if no config file is specified 2019-03-09 11:36:24 +01:00
yrutschle 7baf7f724c Move config dump to stderr and verbose > 4 2019-02-08 22:45:16 +01:00
yrutschle 848b107f2b document conf2struct dependency 2019-02-03 21:13:37 +01:00
yrutschle a7b4462c6e Added 'minlength' option 2019-02-03 14:45:14 +01:00
yrutschle 5b309a9b97 update c2f output 2019-01-27 18:53:36 +01:00
yrutschle d2b64c7f38 Merge pull request #211 from licaon-kter/patch-1
Update verbose option in examples
2019-01-19 08:22:10 +01:00
Licaon_Kter d647b4eb55 And here 2019-01-19 00:27:11 +00:00
Licaon_Kter a584348a55 Update verbose option in examples 2019-01-19 00:26:06 +00:00
yrutschle 530acc7c72 Moved command line parsing to conf2struct 2019-01-12 21:33:44 +01:00
yrutschle dbc0667ad3 removed obsolete declarations 2018-12-08 22:55:03 +01:00
yrutschle e8654da78c Moved configuration file parsing to conf2struct, which
abstract all the tedious bits of reading the settings from
the file into a structure.
2018-12-07 08:41:40 +01:00
yrutschle 343b0a0fbf reactivate tests 2018-12-07 08:40:30 +01:00
yrutschle ad0adfb0e1 re-integrate command line support 2018-12-07 08:32:36 +01:00
yrutschle 33ab9d535d code cleanup and adaptation of regex probe 2018-12-04 23:11:04 +01:00
yrutschle d3d4fd657a moved config parse to c2s code 2018-12-03 11:02:20 +01:00
yrutschle e2fddf17fc updated description to better match our maturity 2018-11-29 18:31:55 +01:00
yrutschle e7ce929020 config file now read to struct with c2s; command line no longer works 2018-11-29 11:56:33 +01:00
yrutschle 7af31c45c9 fix tests so that fragmented tests actually work 2018-11-29 11:52:25 +01:00
Yves Rutschle 4ae2e62d25 v1.20 2018-11-20 22:58:41 +01:00
yrutschle 8ec9799ca0 fix ssl tests 2018-11-04 22:59:01 +01:00
yrutschle 80ad31aec0 refactoring: replace magic constants with symbols 2018-11-04 22:25:16 +01:00
yrutschle d6c714166a only try to parse TLS extensions if settings are actually set 2018-09-30 21:35:45 +02:00
yrutschle aa77922ffd turn ssl setting from command line to tls (ssl no longer exists) 2018-09-30 21:34:22 +02:00
yrutschle 2ee0088c5f turn ssl setting from configuration file to tls (ssl no longer exists) 2018-09-30 20:20:06 +02:00
yrutschle f480eb6c7d refactoring: simpler TLS extension parsing algorithm 2018-09-28 13:58:33 +02:00
yrutschle 6431bb7e35 refactoring: changed magic numbers for use_alpn to named bitfields 2018-09-23 22:29:25 +02:00
yrutschle 6d6ea50066 Merge branch 'master' of https://github.com/yrutschle/sslh 2018-09-23 21:57:16 +02:00
yrutschle 71265a8477 Merge pull request #201 from astiob/probe-strlen
Fix incorrect strncmp length in HTTP method probing
2018-09-23 21:56:51 +02:00
yrutschle 0003680137 remove old tls and ssl targets, only use alpn/sni probe also for TLS with no extensions 2018-09-23 12:07:06 +02:00
Oleg Oshmyan e8f0d3ea53 Fix HTTP method probing 2018-09-22 15:50:40 +03:00
yrutschle a5d00568b5 Merge pull request #199 from rom1dep/sni_alpn_errmatching
tls: proposed fix for incorrect SNI/ALPN matching
2018-08-30 23:16:28 +02:00
yrutschle ffe9971624 test suite for SNI/ALPN with multiple targets and all combinations covered 2018-08-30 19:50:53 +02:00
Yves Rutschle 1693436cc3 automatic test for SNI/ALPN (single target) 2018-08-29 19:18:23 +02:00
Romain DEP. e42f670112 tls: proposed fix for incorrect SNI/ALPN matching 2018-08-21 22:36:01 +02:00
Yves Rutschle 60df92c2b2 prevent repeated reads on broken sockets 2018-08-14 23:05:49 +02:00
Yves Rutschle 8ad32816a6 last_p might be used uninitialised if last probe has no probe set (which I don't think might happen, but anyhow...) 2018-08-14 22:52:52 +02:00
Yves Rutschle 677e385fec new probing algorithm 2018-08-13 22:29:09 +02:00
Yves Rutschle b6db83a701 also test probes when no fragmentation occurs 2018-08-12 21:45:42 +02:00
Yves Rutschle 3a17bd6832 removed obsolete tests 2018-08-12 21:35:42 +02:00
Yves Rutschle 94911c1c2a cleaner framework to test all probes methodically (not all tests work as the probe code needs to be reworked) 2018-08-11 23:13:24 +02:00
Yves Rutschle d7889588da Merge branch 'master' of https://www.github.com/yrutschle/sslh 2018-08-04 22:34:20 +02:00
Yves Rutschle ad2b595280 Test suite: some cleanup and use config file instead of command-line parameters 2018-08-04 22:33:02 +02:00
Yves Rutschle 9df1ab8404 fix man page on timeout default protocol 2018-07-31 19:54:46 +00:00
yrutschle 10fb0bce6f Merge pull request #192 from candrews/patch-5
Include common.h in systemd-sslh-generator.c
2018-07-11 13:18:50 +02:00
Craig Andrews eb53c45351 Include common.h in systemd-sslh-generator.c
Fixes https://github.com/yrutschle/sslh/issues/188
2018-07-06 12:30:28 -04:00
Yves Rutschle a1cc399ae5 fix syslog facility name check 2018-07-03 12:22:03 +02:00
yrutschle 108a9780d8 Merge pull request #187 from msantos/segfault
Fix potential segfaults (unverified malloc() returns) and other robustness issues
2018-06-18 16:45:51 +02:00
Michael Santos 9228171eb0 config: exit if list element is invalid 2018-06-18 10:35:28 -04:00
Michael Santos 8ce2b2ea05 Check memory allocations succeed 2018-06-18 10:35:28 -04:00
Michael Santos 4c132e3c8d config: segfault parsing invalid sni/alpn
Check return value of config_setting_get_string_elem() for error
before passing the result to strlen():

~~~ segfault.conf
protocols:
(
 { name: "tls"; host: "localhost"; port: "8443";  sni_hostnames:  [ 0 ];
}
);
~~~
2018-06-18 10:35:28 -04:00
Michael Santos cfd0163a5b main_loop: initialize in_socket
in_socket may be used uninitialized if no addresses are available.

~~~
sslh-select.c:415:8: warning: Function call argument is an uninitialized value
                            check_access_rights(in_socket, cnx[i].proto->service)) {
                            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~
2018-06-18 10:35:28 -04:00
Michael Santos 5cf591a254 Avoid segfault with malformed IPv6 address
A literal IPv6 address without a trailing bracket will result in a write
past the end of the address buffer:

~~~ segfault.conf
protocols:
(
 { name: "tls"; host: "["; port: "8443"; }
);
~~~

~~~
$ sslh-select -p 127.0.0.1:443 --foreground -F./segfault.conf
[: no closing bracket in IPv6 address?
Segmentation fault (core dumped)
~~~
2018-06-18 10:35:28 -04:00
Michael Santos c179d9a57b start_listen_sockets: exit if no addresses
Do not allocate a 0 byte buffer if no addresses are available:

    common.c:122:14: warning: Call to 'malloc' has an allocation size of 0 bytes
       *sockfd = malloc(num_addr * sizeof(*sockfd[0]));
		 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2018-06-18 10:35:28 -04:00
yrutschle 336e8bb9d1 Merge pull request #186 from Revertron/patch-1
Fixed a typo
2018-06-14 16:36:13 +02:00
Roman 0ada00474b Fixed a typo
Just a typo in usage text.
2018-06-14 14:31:50 +02:00
Yves Rutschle 95e8f5731c document magic constant in socks5 probe 2018-06-13 12:40:02 +00:00
Yves Rutschle dfd9e14866 fix socks5 probe 2018-06-13 09:52:49 +02:00
yrutschle 552723cc5f Merge pull request #180 from sanmai/patch-1
Update README.md
2018-06-01 10:31:34 +02:00
Alexey Kopytko 09aaf39e25 Update README.md
Removed redundant dollar signs since they're not used anywhere else in the README.
2018-06-01 15:13:50 +09:00
Yves Rutschle 7acf9627ee fix memory leak when using transparent proxying 2018-05-29 12:38:57 +02:00
Yves Rutschle b8e63a4d9d fixed systemd config to correct path 2018-05-26 17:02:15 +00:00
yrutschle f451cc8bed Merge pull request #172 from WeirdCarrotMonster/master
Added support for socks5 protocol
2018-04-16 22:38:06 +02:00
Eugene Protozanov a43dd11fc9 Added support for socks5 protocol 2018-04-16 15:27:31 +04:00
yrutschle 3a61c8b0b1 Merge pull request #167 from HighwindsHipsApp/master
Fixed parent/child usage after initial fork in sslh-fork.c
2018-02-11 18:42:21 +01:00
Josh Tway 0c928fedbb Fixed issue in sslh-fork.c where the parent was being used instead of the child after forking. This was breaking multiple unit tests (on CentOS 7 at least) 2018-02-08 17:22:22 -05:00
Yves Rutschle 1a6ba5edc0 fix IPv6 parse error introduced in 7bf3e12c30 2018-01-27 22:59:52 +01:00
Yves Rutschlé 83be63d282 v1.19 2018-01-20 18:40:15 +00:00
Yves Rutschlé 3ab2acdafa fix fork failure handling 2018-01-11 14:45:32 +00:00
Yves Rutschlé c8c6688d16 add file and line number upon error messages 2018-01-10 22:10:19 +00:00
Yves Rutschlé 9a85efded5 log error message in case fork fails 2018-01-08 18:54:01 +00:00
Yves Rutschlé 7bf3e12c30 Don't clobber data in libconfig space, copy it before
changing it. So far it worked, but really that's not
respecting the contract.
2018-01-07 16:43:50 +00:00
Yves Rutschlé bc72c4ac42 clarify regex examples are just examples 2018-01-07 15:42:52 +00:00
Yves Rutschlé fc9cb27746 fix ALPN configuration logic 2018-01-06 22:43:16 +00:00
Yves Rutschlé 2c3518beb4 configuration examples for fork option 2018-01-06 22:42:39 +00:00
yrutschle 141e4b4f83 Merge pull request #105 from vapier/master
add chroot support
2018-01-03 20:34:05 +01:00
Mike Frysinger 0fb4c6b2ad add chroot support
This allows people to chroot sslh into a path to further harden it.

We have to rework the user logic a bit because we need to look up
the user details *before* we chroot (as we need to read /etc/passwd
files), but do the actual priv dropping *after* we chroot (so we
have permission to make the actual chroot call).

Similarly, we need to open the syslog before we drop privs because
/dev/log won't be available inside the chroot.
2018-01-03 10:19:59 -05:00
yrutschle d9541392f8 Merge pull request #139 from tmolitor-stud-tu/master
Better documentation of transparent proxy support with more features
2018-01-02 23:24:46 +01:00
yrutschle 24cd3d0974 Merge pull request #161 from pali/master
Ensure that IPv6 socket would listen only for IPv6 connections
2018-01-02 23:19:00 +01:00
yrutschle d26eab728c Merge pull request #143 from astiob/select-fork
sslh-select: support forking for particular protocols
2018-01-02 22:26:58 +01:00
yrutschle 53e00c27ff Merge pull request #159 from rdebath/patch-5
Translate systemd C99 and C++ code to old C
2018-01-02 22:23:19 +01:00
yrutschle 063169c167 Merge pull request #162 from vapier/adb
adb: update protocol sniffer
2018-01-02 22:21:19 +01:00
Prameet Shah 79ca4df0d7 adb: update protocol sniffer
Modified is_adb_protocol in patch.c to check if initial host->device
packet sends an empty message for reasons unknown.  This was introduced
in ADB master in https://android-review.googlesource.com/c/342653.
2018-01-02 06:25:27 -05:00
Pali Rohár 3db5e127fd Ensure that IPv6 socket would listen only for IPv6 connections
When transparent mode is enabled and sslh listening on :: IPv6 address then
source origin address is propagated to target application independently if
connection is IPv4 or IPv6.

On Linux by default IPv6 socket can accept also IPv4 connections. More
applications, including OpenSSH server do not accept IPv4 connections on
IPv6 socket and therefore such transparent configuration does not work.
On BSD systems it is turned off by default due to security reasons.

This patch disables IPv4 connections on IPv6 listening sockets. If somebody
needs to have sslh listening on both IPv4 and IPv6 addresses, then still it
is possible by specifying multiple --listen arguments.

I think it is more misleading if option --listen :::443 cause listening on
both IPv4 and IPv6 addresses even IPv4 address was not specified. This can
also cause security related problems for people who do not know about this
fact as documentation does not mentioned this behavior.
2017-12-28 21:31:15 +01:00
Robert de Bath 981091e39d Translate systemd C99 and C++ code to old C 2017-12-17 16:37:00 +00:00
Yves Rutschle 0fc6bc8d12 Revert "Adjust linking so that wrapper libraries are static."
This reverts commit 021eb836e4.

That commit introduces regression, see
https://github.com/yrutschle/sslh/issues/157#issuecomment-352260802
for discussion.
2017-12-17 16:59:57 +01:00
Yves Rutschle b36fc73b7a log timeouts 2017-12-17 14:57:45 +01:00
Yves Rutschle a7f0c456ab die if target cannot be resolved (otherwise, we segfault when printing the settings or later) 2017-12-17 14:55:51 +01:00
yrutschle 7808a3a766 Merge pull request #142 from astiob/select-bugs
Fix several bugs in sslh-select
2017-12-16 19:51:23 +01:00
yrutschle f5b1b881a4 Merge pull request #145 from ariera/master
Doc update about Transparent proxy support
2017-12-16 19:11:34 +01:00
yrutschle f8a16c7a29 Merge pull request #146 from guusdk/SSLv2-clienthello
Allow SSLv2 CLIENT-HELLO (without SSL 2.0)
2017-12-16 19:09:25 +01:00
yrutschle b33c65ed53 Merge pull request #96 from candrews/patch-1
Harden the systemd service
2017-12-16 19:04:52 +01:00
yrutschle 2f8e635b67 Merge pull request #97 from candrews/patch-2
make sure the files using version.h depend on it being generated first
2017-12-16 19:01:28 +01:00
yrutschle b8851d6714 Merge pull request #98 from candrews/patch-3
Build systemd-sslh-generator if USESYSTEMD is set
2017-12-16 18:59:36 +01:00
Craig Andrews e33124718e Harden the systemd service 2017-12-12 16:40:53 -05:00
yrutschle 85b94c3259 Merge pull request #153 from rdebath/master
Move hexdump to verbose level 2
2017-11-28 21:05:50 +01:00
Robert de Bath 4e790e074f Move hexdump to verbose level 2
From the command line you use two "-v" options or in the configuration
file you replace the boolean "verbose:true" with an integer "verbose:2".
2017-11-27 21:05:07 +00:00
Yves Rutschle 6ca1ee7bfd remove leftover debug messages 2017-11-26 20:10:05 +01:00
yrutschle e66e443d5e Merge pull request #151 from rdebath/patch-3
Hexdump for verbose mode.
2017-11-24 13:55:53 +01:00
yrutschle faa928b75a Merge pull request #150 from rdebath/patch-2
Get libpcre working (and by default)
2017-11-24 13:53:52 +01:00
yrutschle 2ad99fd36f Merge pull request #149 from rdebath/patch-1
Fixup compile using -std=c90
2017-11-24 13:52:22 +01:00
Robert de Bath 64485d7a58 Send hexdump to stderr like other verbose logs. 2017-11-23 20:52:54 +00:00
orbitarm 5b756ebd0a verbose: dump hex value of packet
uses the hexdump() function to display the hex value of each probed
packet, making it easy to create regex rules for unsupported connections
2017-11-23 20:52:54 +00:00
Robert de Bath 021eb836e4 Adjust linking so that wrapper libraries are static. 2017-11-23 20:51:57 +00:00
Robert de Bath cb90cc97ae Default to using libpcre and actually use it
as libpcre has to better binary support.

Note, just linking libpcre only has no effect, the posix functions are
provided by libpcreposix.

Use "make USELIBPCRE=" to turn libpcre off and link POSIX library.
2017-11-23 20:51:57 +00:00
Robert de Bath 338daafe87 Use REG_EXTENDED for regex matching
The "7 regex" manual page called 'Basic' regular expressions "Obsolete".

It also matches the pcre expressions slightly better.
2017-11-23 20:51:57 +00:00
Robert de Bath 9fcbe8c7ea Fixup compile using -std=c90 2017-11-23 20:50:49 +00:00
yrutschle 2a7bafdd7f Merge pull request #147 from jmccrohan/master
tls: ensure hostname is always null-terminated
2017-11-13 23:03:12 +01:00
Jonathan McCrohan 3f5d9a4168 tls: ensure hostname is always null-terminated
6cc3382 introduced a potential buffer overflow. Ensure that hostname is
always null-terminated. (Issue #135)

Signed-off-by: Jonathan McCrohan <jmccrohan@gmail.com>
2017-11-13 00:15:41 +00:00
Guus der Kinderen 1f98b97756 Allow SSLv2 CLIENT-HELLO (without SSL 2.0)
The existing TLS probe is documented to ignore SSL 2.0, citing RFC 6176 as a reason.
RFC 6176 does prohibit the usage of SSL 2.0, but does allow for ClientHello messages
in the version 2 CLIENT-HELLO format (as long as those are used to negotiate the use
of a higher protocol).

This commit extends the TLS probe, by making it accept SSL v2 ClientHello messages
that negotiate a version of SSL/TLS 1.0 or higher (which is the same version range
as the original code).
2017-11-10 19:47:07 +01:00
Alejandro Riera 09d11e3bc8 Doc update about Transparent proxy support
Advise users to save the configuration of `iptables` and `ip` rules and routes or they risk loosing it after a reboot and/or crash.
2017-11-08 11:54:26 +01:00
Oleg Oshmyan 2a70470f13 sslh-select: reduce CPU and memory usage in forked processes 2017-10-28 23:27:10 +03:00
Oleg Oshmyan 2544f20bdf sslh-select: support forking for particular protocols
To keep the code simple, use the same event loop in the child process
as in the parent process but close all irrelevant file descriptors.
2017-10-28 23:27:06 +03:00
Oleg Oshmyan 60b11e4964 Fix defer_write when deferred_data != begin_deferred_data
I think this currently never happens, but let's
not wait until it starts happening and blows up.
2017-10-28 23:13:29 +03:00
Oleg Oshmyan b7fafb5039 sslh-select: invoke FD_CLR on fd before closing fd
POSIX requires the fd argument to any FD_ macro to be valid.
2017-10-28 23:13:28 +03:00
Oleg Oshmyan b56f302b85 sslh-select: simplify some code 2017-10-28 23:13:28 +03:00
Oleg Oshmyan 684c9afcc6 sslh-select: actually close socket on error in accept_new_connection
Previously, it was leaked (and the client was left waiting for a timeout).
2017-10-28 23:13:28 +03:00
Oleg Oshmyan a3df50f31f sslh-select: fix connections with deferred data after connect_queue
Previously, if some data was still deferred after the connect_queue
call, the server side of the connection would never start being
monitored for reads, while the client side kept being monitored
and new data from the client could be sent to the server before
the previously deferred data.
2017-10-28 23:13:28 +03:00
Thilo Molitor d243d36add Fix ipv6 config copy-paste errors 2017-10-10 07:36:09 +02:00
Thilo Molitor 74767cb781 Some cleanup 2017-10-10 07:22:44 +02:00
Thilo Molitor 2a76b520d5 Add better documentation of transparent proxy support.
This allows for some more generalized configs.
You don't need to specify ports anymore and still can
connect directly to the running services if you want.

It also allows you to use "localhost" as destination in your sslh config,
something that wasn't possible with the old scheme.
2017-10-10 07:07:28 +02:00
Yves Rutschlé 0929d39a34 move Let's encrypt config before TLS catchall 2017-09-24 19:55:38 +00:00
Yves Rutschle f4d2a8d2ad fix logging to specified facility 2017-07-22 17:20:45 +02:00
Yves Rutschle aa06261d70 added syslog_facility option 2017-07-21 22:46:24 +02:00
Yves Rutschle dd900ebf3e fail gracefully if target protocol decription is incomplete 2017-07-09 20:51:53 +02:00
Yves Rutschlé 21f524f711 Add support for wildcard ALPN/SNI values 2017-06-12 21:05:12 +00:00
Jonathan McCrohan 1e65088b7e example.cfg: Add Let's Encrypt support to config
Provides a sample config for Let's Encrypt using the tls-sni-*
challenges. Requires wildcard support added in 6cc3382.

Signed-off-by: Jonathan McCrohan <jmccrohan@gmail.com>
2017-06-06 01:37:07 +01:00
Jonathan McCrohan 6cc33820d1 tls: permit wildcard ALPN/SNI values
Use fnmatch(3) to provide support for glob style wildcard values in the
ALPN and SNI parameters of the TLS probe.

Signed-off-by: Jonathan McCrohan <jmccrohan@gmail.com>
2017-06-06 01:11:29 +01:00
Yves Rutschle 00d5872aa1 ignore brackets in hostname in config files 2017-04-21 22:33:02 +02:00
Yves Rutschle 7d561af423 allocate listen[] before writing to it... 2017-04-18 21:04:30 +02:00
Yves Rutschle cce42c6882 re-indent 2017-04-18 20:53:19 +02:00
Yves Rutschle b0f4e24ce0 IP_FREEBIND: real fix, ignore catastrophic previous checkin 2017-04-06 17:13:11 +02:00
Yves Rutschle 078827ad3f Some systems define IP_FREEBIND but don't implement it which result in setsockopt() failing. No need to die in that case, just ignore the error. 2017-04-06 16:26:27 +02:00
yrutschle 4413284420 Merge pull request #120 from yann-morin-1998/yem/parallel-make
Makefile: fix parallel build
2017-02-23 22:54:55 +01:00
Yann E. MORIN 70a2ea926e Makefile: fix parallel build
version.h is included by some .o files, but it is generated. As such, it
must be a dependency of those .o files.

Rather than filter exactly which .o file needs it, just add a generic
dependency for all .o files on version.h.

Signed-off-by: "Yann E. MORIN" <yann.morin.1998@free.fr>
2017-02-23 22:49:31 +01:00
Yves Rutschle e4a4e04bf8 Clarify no space after -F (Issue 108, take 2) 2017-01-08 13:00:19 +01:00
Yves Rutschle 067f5d7646 Revert "clarify no space after -F (issue 108)"
This reverts commit f02ce3821c.

That commit accidently imported code that broke transparent
proxying.
2017-01-08 12:54:34 +01:00
yrutschle 718fe0e2e9 Merge pull request #106 from nckx/man-trivialities
Fix a few typos in the man page
2016-12-19 21:55:03 +01:00
yrutschle 864b7badd2 Merge pull request #115 from jimt/patch-1
Fix README typos.
2016-12-19 21:39:09 +01:00
Jim Tittsler 234c088324 Fix README typos. 2016-12-19 10:24:31 +09:00
Yves Rutschlé f02ce3821c clarify no space after -F (issue 108) 2016-11-06 19:44:47 +00:00
Tobias Geerinckx-Rice e492c2808c Fix a few typos in the man page 2016-10-19 02:41:12 +02:00
Craig Andrews a4d2a53a47 Build systemd-sslh-generator if USESYSTEMD is set 2016-06-24 20:30:15 -04:00
Craig Andrews efba558967 make sure the files using version.h depend on it being generated first 2016-06-24 20:11:13 -04:00
yrutschle b4cb910438 Merge pull request #92 from mscherer/complete_man
Add more options in the synopsis
2016-06-05 18:37:22 +02:00
Michael Scherer 975560aa4a Add more options in the synopsis 2016-06-04 11:57:08 +02:00
Yves Rutschlé 63a83cf041 fix SNI/ALPN option parsing (issue 90) 2016-05-20 07:52:24 +00:00
Yves Rutschlé 245fd26459 fix compiler warnings 2016-05-03 08:37:23 +00:00
yrutschle 9cc47a157d Merge pull request #69 from ViKingIX/fix_local_conn
Fix the connection problem in transparent mode.
2016-04-28 11:55:56 +02:00
yrutschle 09b5c84d34 Merge pull request #86 from hogarthj/fix_readme
Merge error in README.md mixing sections
2016-03-31 16:28:32 +02:00
James Hogarth e6cb3596d1 Merge error in README.md mixing sections 2016-03-30 11:48:45 +01:00
Yves Rutschle 38447c8158 v1.18 2016-03-29 21:19:05 +02:00
yrutschle 8f39c106e1 Merge pull request #56 from hogarthj/master
Initial addition of systemd socket based activiation
2016-02-05 16:46:47 +01:00
Yves Rutschle 414ed7de11 Support keepalive for connections on the connecting side 2016-02-04 09:19:54 +01:00
Yves Rutschle 1b9937b293 Support keepalive for connections on the listening side 2016-02-02 21:07:47 +01:00
Yves Rutschle 1814bcb43c Fixed typo in example configuration files 2016-02-02 20:53:10 +01:00
James Hogarth b3f48d9876 Add systemd capabilities to sslh, rebased on current master 2016-01-29 16:20:54 +00:00
Yves Rutschle 555005f455 Complies to DCL37-C (Issue 59) 2016-01-26 18:49:57 +01:00
Yves Rutschle e511534b57 Make USELIBPCRE not the default 2016-01-22 17:09:02 +01:00
Yves Rutschle e5cb33fcb7 Add PCRE support for musl 2016-01-22 16:41:36 +01:00
yrutschle e7d3133ba5 Merge pull request #74 from moparisthebest/master
Add ALPN protocol based probe
2016-01-22 16:21:32 +01:00
Yves Rutschle 0b6cc0d909 Added a note on configuring transparent proxying for both IPv4 and IPv6 2016-01-22 11:25:53 +01:00
moparisthebest 8af039d3eb Add ALPN protocol based probe 2016-01-05 00:32:10 -05:00
Yves Rutschle 8758a298ba Changed connection log to include the name of the probe that triggered. 2015-12-15 16:06:14 +01:00
Yves Rutschle 2cb424c646 Added log_level option to configuration file, which switches off log at each connection 2015-12-15 15:51:18 +01:00
yrutschle 24612835c3 Merge pull request #70 from taligentx/master
Fix daemon start syntax
2015-10-20 10:58:58 +02:00
Nikhil Choudhary b09c3aab31 Fix daemon start syntax
Per the changelog for 1.17: argument to -F can no longer be separated from the option by a space, e.g. must be -Ffoo.cfg).  This fixes the `/etc/sslh/sslh.cfg:file I/O error` message if the config file is stored in `/etc/sslh.cfg` as given by Readme.md.
2015-10-19 13:40:51 -05:00
ViKing b108809a78 Fix the connection problem in transparent mode.
When the source and destination are the same, the bind_peer() will
fail, thus end the connection. Therefore a check of all the interface
IPs are checked to skip bind() if they are the same.
2015-10-07 00:10:03 +08:00
yrutschle 389ab9fbff Merge pull request #67 from eehakkin/master
Fix access rights checking to work with IPv6.
2015-09-28 14:59:16 +02:00
Eero Häkkinen 717c285b31 Fix access rights checking to work with IPv6. 2015-09-27 22:14:00 +03:00
John Regan ab3324be47 Enable PCRE as RegEx Library 2015-08-13 14:28:17 -04:00
Yves Rutschle 4cbaf447b5 Print error message upon non-existent configuration file 2015-07-28 15:14:21 +02:00
Yves Rutschle ca461ea077 Added support for RFC4366 SNI (Server Name Indication). Changed configuration file format accordingly. 2015-07-17 15:05:06 +02:00
Yves Rutschle 8fdaf6eb08 changed configuration file to accomodate SNI in a cleaner way 2015-07-17 15:04:04 +02:00
Yves Rutschle 5886bd2d43 Print error message upon non-existent configuration file 2015-07-16 17:43:05 +02:00
Yves Rutschle 77ef29358d make code C-compliant 2015-07-15 15:09:39 +02:00
Yves Rutschle 9475d9689b Comment for SNI inclusion 2015-07-15 15:02:37 +02:00
Yves Rutschle fbebdaf66c Add support for Server Name Indication (SNI, RFC4366) 2015-07-15 14:07:16 +02:00
Yves Rutschle fecfb170c8 added reference to Tinc documentation 2015-07-15 13:34:53 +02:00
moparisthebest b988540105 Add SNI hostname based probe 2015-07-12 23:10:53 -04:00
Yves Rutschle 3aefaf3004 Added Makefile option to build without libpcre 2015-07-09 15:31:42 +02:00
yrutschle ba945f1a8f Merge pull request #52 from Somasis/master
Makefile: use more variables for install directories
2015-06-22 11:59:52 +02:00
Kylie McClain eb3d3be3ab Makefile: use more variables for install directories 2015-06-22 01:51:36 -04:00
yrutschle 66f85dc608 Merge pull request #51 from cernekee/adb
Add builtin handler for Android Debug Bridge (ADB) protocol
2015-06-10 09:26:55 +02:00
Kevin Cernekee 3469f56012 Add builtin handler for Android Debug Bridge (ADB) protocol
This allows Android devices to run multiple services on one port.  A
common use case involves muxing SSH for SCP / SFTP, and ADB for
sideloading packages or running CTS.

Signed-off-by: Kevin Cernekee <cernekee@chromium.org>
2015-06-09 15:30:27 -07:00
yrutschle 7c35ef8528 Merge pull request #49 from dunn/master
Makefile: fix `install` for Mac OS
2015-05-18 11:12:45 +02:00
Alex Dunn 3bad96865d Makefile: fix install for Mac OS 2015-05-14 23:14:40 -07:00
yrutschle 728181109c Merge pull request #47 from hogarthj/master
Build fails in CentOS6 - this fixes it
2015-04-18 10:35:02 +02:00
James Hogarth 2192b28303 Check line number of error so that this works with libconfig-1.3.2 in CentOS6 2015-04-17 23:40:57 +01:00
yrutschle 77a74f0c52 Merge pull request #45 from muxator/patch-1
typo: "transparent proying" -> "transparent proxying"
2015-03-15 12:01:58 +01:00
muxator 48164c4d77 typo: "transparent proying" -> "transparent proxying" 2015-03-15 00:44:39 +01:00
Yves Rutschle 3550cbe77c Finalised v1.17 2015-03-09 21:51:39 +01:00
yrutschle 130348ed48 Merge pull request #44 from antisocialdalek/antisocialdalek-fix-xmpp
add longer check for xmpp preamble
2015-03-09 21:46:18 +01:00
Justin Matlock bdeccfd9ff add longer check for xmpp preamble
original wasn't catching the preamble from Adium or Pidgin XMPP clients, because of a newline after the initial <xml> line. Grew the length of the check string so it'd see the word 'jabber' faster.
2015-03-06 02:58:52 -05:00
yrutschle ce7c5b1ba2 Merge pull request #41 from gapato/patch-1
Fix typo for CentOS install instructions in README
2015-02-04 14:00:12 +01:00
gapato 21552fc176 Fix typo for CentOS install instructions in README 2015-02-04 12:53:32 +01:00
Yves Rutschle 88af6ebaee Updated configuration info and startup scripts to use the configuration file rather than command line parameters 2015-01-01 18:59:05 +01:00
Yves Rutschle 43d2db9123 Fix libconfig issue with integer parameters on x64 2015-01-01 18:35:26 +01:00
Yves Rutschle d91cd59bba Documented configuration trick to have both transparent proxying while still retaining the ability to connect to ssh directly 2015-01-01 18:31:10 +01:00
Yves Rutschle c03168042f Added IPv6 transparent proxying instructions 2014-12-31 14:53:59 +01:00
Yves Rutschle 2705426f63 Attribute previous commit 2014-12-27 19:51:20 +01:00
Yves Rutschle 0458c9840b Use portable way of getting modified time 2014-12-27 19:49:51 +01:00
Yves Rutschle bb4aeb446a Use default configuration filename 2014-12-27 11:57:27 +01:00
Yves Rutschle 74de4f4fd2 Transparent proxy support for FreeBSD (attribution) 2014-12-25 20:15:52 +01:00
Yves Rutschle 56fdc6b4af Transparant proxy support for FreeBSD 2014-12-25 20:08:24 +01:00
yrutschle b6f4c04c36 Merge pull request #25 from guikcd/remove_cant_bind_address_test
Disable Can't bind to address test since IP_FREEBIND allow us to do that
2014-12-25 19:57:47 +01:00
Yves Rutschle b9ddfb4c7a Support RFC5952-style IPv6 addresses 2014-12-22 18:19:02 +01:00
Aaron Madlon-Kay 8c3362e9ce Use portable way of getting modified time 2014-11-22 23:46:50 +09:00
Ruben van Staveren ece6e28e45 #ifdef IP_BINDANY/IPV6_BINDANY cases 2014-07-24 17:29:53 +02:00
Ruben van Staveren 0d8e2438de Correct markdown 2014-07-22 21:43:03 +02:00
Ruben van Staveren 36cf99697b Add instruction for FreeBSD 2014-07-22 20:30:52 +02:00
Ruben van Staveren ddc1efed89 Merge branch 'freebsd_transparent' of https://github.com/rvstaveren/sslh into freebsd_transparent 2014-07-22 20:06:32 +02:00
Ruben van Staveren e2fc091482 When transparent, make sure both connections use the same address family 2014-07-22 20:05:25 +02:00
Ruben van Staveren 42425a8373 Have USELIBWRAP redefineable 2014-07-22 20:05:25 +02:00
Ruben van Staveren e246536be2 FreeBSD way of doing transparent proxy: work in progress 2014-07-22 20:05:25 +02:00
Ruben van Staveren 7d23a55236 When transparent, make sure both connections use the same address family 2014-07-22 19:36:40 +02:00
Ruben van Staveren dedb3672d7 Have USELIBWRAP redefineable 2014-07-22 19:36:29 +02:00
Guillaume Delacour 21a6d3c3ae Disable Can't bind to address test since IP_FREEBIND allow us to do that 2014-07-15 16:22:37 +02:00
Yves Rutschle 9a0a9b9492 Clarified that sslh uses LOG_AUTH facility for logging in manual page 2014-07-15 11:26:16 +02:00
Ruben van Staveren b6de2904f0 FreeBSD way of doing transparent proxy: work in progress 2014-06-20 14:11:25 +02:00
Yves Rutschle d10b539a5a fixed obsolete README reference to -o option 2014-04-19 13:10:12 +02:00
Yves Rutschle 48d4d81e0c minor corrections to usage string 2014-04-19 10:41:17 +02:00
Yves Rutschle 36e05640c0 added -F description to man page 2014-04-19 10:40:53 +02:00
Ondřej Kuzník 7876bddff3 Fix regex probes always matching (#19) 2014-04-09 19:18:52 +01:00
Yves Rutschle 6fb234f85e added fail2ban configuration examples 2014-03-30 18:51:21 +02:00
Yves Rutschle 7d6cac73d4 added transparent option to man page and help 2014-03-30 18:25:03 +02:00
Yves Rutschle 621f0718dd added license file 2014-03-30 18:09:16 +02:00
Yves Rutschle 426797f9c0 call setgroups before setgid 2014-03-30 17:28:00 +02:00
Yves Rutschle 53550ff21e fix errors in previous commit... 2014-02-24 17:52:58 +01:00
Yves Rutschle 9beacc63f9 use directory version when compiling from a tarball without git 2014-02-23 10:41:47 +01:00
Jason Cooper 62cbb55b8e genver.sh: use /bin/sh for portability
Signed-off-by: Jason Cooper <jason@lakedaemon.net>
2014-02-17 19:08:02 +01:00
yrutschle 27567c4804 Merge pull request #14 from belobrov-andrey/fd_leak_fix
Fixed possible file descriptor leak.
2014-02-16 13:11:19 +01:00
Belobrov Andrey ff070a6b46 Fixed possible file descriptor leak. 2014-02-14 08:32:38 +04:00
Yves Rutschle 9d2deff6ad Changelog prepared for v1.16 2014-02-11 22:06:01 +01:00
Yves Rutschle 6bcb5c83f2 libcap support: print out process capabilities at startup if verbose 2014-02-09 21:39:27 +01:00
Yves Rutschle 2d3b6c4abd fix Markdown documentation for libcap 2014-02-09 20:50:03 +01:00
Yves Rutschle 4dfa694e8a Merged libcap patch 2014-02-09 20:34:26 +01:00
yrutschle e6318ddde0 Merge pull request #12 from vapier/master
sslh-fork: close all listening sockets in shoveler
2014-02-09 13:59:10 +01:00
Yves Rutschle 67c34a7460 set IP_FREEBIND if available to bind to non-existent interfaces 2014-02-09 13:29:49 +01:00
Mike Frysinger 71ce82815c sslh-fork: close all listening sockets in shoveler
When we're watching multiple sockets, we don't want to just close
the active one we got a connection on before launching the shoveler.
If we want to restart the daemon, we run into problems because the
socket is still in use.  Instead, close all the sockets we were
listening on.

Signed-off-by: Mike Frysinger <vapier@gentoo.org>
2014-01-09 10:16:42 -05:00
Yves Rutschle 5998c9ec1a Do not require --listen when --inetd is specified 2014-01-06 22:21:44 +01:00
Yves Rutschle 45996cc1ee minor typesetting fix to manual page 2014-01-06 22:07:33 +01:00
Yves Rutschle 56944e4d38 Generate version tag based on file modification date if git is not present 2013-11-23 16:46:54 +01:00
yrutschle 9c3a838cc5 Merge pull request #8 from nbraud/readme
Markdownify the README
2013-11-05 23:10:24 -08:00
Nicolas Braud-Santoni b24f9820f9 Markdownify the README 2013-11-05 22:34:48 +01:00
Sebastian Schmidt 009faa64b7 Implement libcap support
Use libcap for saving CAP_NET_ADMIN (if --transparent is given) over a
setuid(). We don’t need CAP_NET_BIND_SERVICE as the listening sockets
are established before dropping root.
2013-10-20 21:16:56 +02:00
Yves Rutschle 3f386b6541 initiated TODO list 2013-10-06 12:09:52 +02:00
Yves Rutschle fb0760dd72 Probes made resilient to packets that are too short, or
contain NULLs.
2013-09-28 21:39:00 +02:00
Yves Rutschle f2ca4c13a6 ChangeLog entry for the branch 2013-09-28 21:38:33 +02:00
Yves Rutschle 96f5d6387e new test for PROBE_AGAIN; changed deferred_data to begin_deferred_data where appropriate 2013-09-28 21:33:25 +02:00
Ondrej Kuzník 025545aee3 Fix typos and type warnings 2013-09-28 20:49:46 +02:00
Ondřej Kuzník d14dcdee5c Fix build issues when version.h doesn't exist yet 2013-09-28 20:44:08 +02:00
Ondřej Kuzník 66c7d674a0 is a bashism 2013-09-28 20:42:05 +02:00
Ondřej Kuzník e4fb8b8496 defered -> deferred 2013-09-28 20:42:04 +02:00
Ondřej Kuzník d7bbec0dc7 Simplify function signatures 2013-09-28 20:21:48 +02:00
Ondřej Kuzník bcad6fbade Enable the PROBE_AGAIN return code 2013-09-28 20:21:47 +02:00
Ondřej Kuzník dbafd6510d Allow probes to say they cannot decide yet 2013-09-28 20:21:47 +02:00
Ondřej Kuzník c84a6af847 Introduce the probe return codes. 2013-09-28 20:21:47 +02:00
Ondřej Kuzník c5cd91d92c Let defer_write accumulate data 2013-09-28 20:21:47 +02:00
Ondřej Kuzník 708c3b0177 Make probes work even in the face of arbitrary data 2013-09-28 20:21:47 +02:00
Yves Rutschle ce170814f5 fix genver.sh shell version to bash 2013-09-19 09:25:35 +02:00
Yves Rutschle a168461f46 Merged Makefile LDFLAGS changes 2013-09-17 11:04:37 +02:00
Yves Rutschle 5952ca4aaf Make version.h before any other object 2013-09-17 11:01:05 +02:00
Yves Rutschle a54cc1aa83 Make version.h before any other object 2013-09-17 08:41:10 +02:00
Mike Frysinger 2d23cdc9f4 check asprintf return value
The current asprintf usage triggers many warnings like:

sslh-main.c: In function 'print_usage':
sslh-main.c:86:17: warning: ignoring return value of 'asprintf',
	declared with attribute warn_unused_result [-Wunused-result]

Signed-off-by: Mike Frysinger <vapier@gentoo.org>
2013-09-17 00:26:44 -04:00
Mike Frysinger b8ea0699c4 drop_privileges: fix setuid check
The code attempts to check the return of setuid, but forgets to assign
the result variable.

Signed-off-by: Mike Frysinger <vapier@gentoo.org>
2013-09-17 00:23:26 -04:00
Mike Frysinger c54e232673 sslh-main: fix config_lookup_int call
This func takes an int, not a long.  The current code triggers a warning:

sslh-main.c: In function 'config_parse':
sslh-main.c:275:5: warning: passing argument 3 of 'config_lookup_int' from incompatible pointer type [enabled by default]
     if (config_lookup_int(&config, "timeout", &timeout) == CONFIG_TRUE) {
                                               ^
In file included from sslh-main.c:26:0:
/usr/include/libconfig.h:266:12: note: expected 'int *' but argument is of type 'long int *'
 extern LIBCONFIG_API int config_lookup_int(const config_t *config,

Signed-off-by: Mike Frysinger <vapier@gentoo.org>
2013-09-17 00:21:37 -04:00
Mike Frysinger 8252ecf307 Makefile: fix CPPFLAGS handling
This code doesn't respect CPPFLAGS at all.  Fix that and move the
existing -D flags to the right variable.

Signed-off-by: Mike Frysinger <vapier@gentoo.org>
2013-09-17 00:18:15 -04:00
Mike Frysinger 4fafb3d376 Makefile: fix LDFLAGS handling
We need these flags to come before all the objects, not after.
Otherwise, flags that impact handling of input objects do not
show up in time.

This also matches standard build system behavior (e.g. autotools).

Signed-off-by: Mike Frysinger <vapier@gentoo.org>
2013-09-17 00:17:23 -04:00
Jason Cooper 7008a1ede4 cscope: add cscope tagging support
Signed-off-by: Jason Cooper <jason@lakedaemon.net>
2013-09-16 22:02:29 +02:00
Jason Cooper 820e31bfc0 Makefile: add distclean target, remove tags file
Signed-off-by: Jason Cooper <jason@lakedaemon.net>
2013-09-16 22:01:47 +02:00
Jason Cooper f36eb7be39 version.h: dynamically create version number based on git
When building the source from a checked out tag, eg v1.15, VERSION will
equal v1.15.  However, when building from anything other than a tagged
version, you get 'v1.15-4-g50432d5-dirty' meaning I was 4 patches in
front of v1.15, particularly '50432d5' was my current HEAD, and I had
uncommited changes, '-dirty'.

Very useful for folks submitting bug reports on versions they compiled
themselves.

Signed-off-by: Jason Cooper <jason@lakedaemon.net>
2013-09-16 21:56:45 +02:00
Jason Cooper c6adb6a1e1 remove unneeded executable permissions on source files
Signed-off-by: Jason Cooper <jason@lakedaemon.net>
2013-09-16 21:56:38 +02:00
Jason Cooper 97ffa562ce git: add .gitignore file
Signed-off-by: Jason Cooper <jason@lakedaemon.net>
2013-09-16 21:55:26 +02:00
yrutschle 7afccc6565 Merge pull request #4 from julthomas/jth/rhel-init
MINOR: init: Review RH/CentOS init script
2013-08-30 02:53:18 -07:00
yrutschle 22f4a4bc47 Merge pull request #3 from julthomas/jth/config-transparent
MINOR: config: Option --transparent can be set via configuration file
2013-08-30 02:52:24 -07:00
Yves Rutschle f3c5f098ca fixed getpeername causing sslh to quit 2013-08-29 12:15:50 +02:00
Julien Thomas 5ae9ba184c MINOR: init: Review RH/CentOS init script
This is an update of the init scripts originally written by Andre
Krajnik. It is quite similar to other init scripts brought by common
packages in RH/CentOS. This commit also introduces a pretty straight
forward sysconfig file.
2013-08-26 21:11:17 +02:00
Julien Thomas 43a9bc8fd9 MINOR: config: Option --transparent can be set via configuration file
This patch allows to set option --transparent in an SSLH configuration
file. Without it, transparent mode is only possible by passing the
option on the command line.
2013-08-26 21:07:27 +02:00
yrutschle 569c71f6b1 Merge pull request #1 from cicku/patch-1
Fix for RPM distdir build
2013-08-10 01:22:08 -07:00
Christopher Meng bde20dbaa5 Fix for RPM
- RPM doesn't support root, so for the chroot environment we must define a destdir of RPM %{buildroot}.

- Preserve the timestamp.
2013-08-10 15:06:49 +08:00
Yves Rutschle c60696a6d5 Updated Fedora package requirements 2013-08-09 20:18:22 +02:00
Yves Rutschle c02e2d7aee v1.15 release 2013-07-27 16:25:04 +02:00
Yves Rutschle 59c9be54ad Set FD_SETSIZE to 4096 on Cygwin 2013-07-26 18:42:22 +01:00
Yves Rutschle e3159409c0 check fd < FD_SETSIZE 2013-07-25 21:35:27 +02:00
Yves Rutschle 536f7dee83 Changed SOL_IP to more-portable IPPROTO_IP 2013-07-25 21:33:07 +02:00
Yves Rutschle 2781c75ff9 Added tranparent proyxing 2013-07-21 13:46:45 +02:00
Yves Rutschle d02ffcd154 Fixed bug in sslh-select: if socket dropped while defered_data was present, sslh-select would crash. 2013-07-20 00:45:33 +02:00
Yves Rutschle f842e2e081 v1.14: 21DEC2012
Corrected OpenVPN probe to support pre-shared secret
	mode (OpenVPN port-sharing code is... wrong). Thanks
	to Kai Ellinger for help in investigating and
	testing.

	Added an actual TLS/SSL probe.

	Added configurable --on-timeout protocol
	specification.

	Added a --anyprot protocol probe (equivalent to what
	--ssl was).

	Makefile respects the user's compiler and CFLAG
	choices (falling back to the current values if
	undefined), as well as LDFLAGS.
	(Michael Palimaka)

	Added "After" and "KillMode" to systemd.sslh.service
	(Thomas Weißschuh).

	Added LSB tags to etc.init.d.sslh
	(Thomas Varis).
2013-07-10 23:19:33 +02:00
Yves Rutschle 5cd1fa1875 v1.13: 18MAY2012
Write PID file before dropping privileges.

	Added --background, which overrides 'foreground'
	configuration file setting.

	Added example systemd service file from Archlinux in
	scripts/
	https://projects.archlinux.org/svntogit/community.git/tree/trunk/sslh.service?h=packages/sslh
	(Sébastien Luttringer)
2013-07-10 23:16:50 +02:00
Yves Rutschle 9bcb2cdd7a v1.12: 08MAY2012
Added support for configuration file.

	New protocol probes can be defined using regular
	expressions that match the first packet sent by the
	client.

	sslh now connects timed out connections to the first
	configured protocol instead of 'ssh' (just make sure
	ssh is the first defined protocol).

	sslh now tries protocols in the order in which they
	are defined (just make sure sslh is the last defined
	protocol).
2013-07-10 23:15:38 +02:00
Yves Rutschle 26b4bcd089 v1.11: 21APR2012
WARNING: defaults have been removed for --user and
	--pidfile options, update your start-up scripts!

	No longer stop sslh when reverse DNS requests fail
	for logging.

	Added HTTP probe.

	No longer create new session if running in
	foreground.

	No longer default to changing user to 'nobody'. If
	--user isn't specified, just run as current user.

	No longer create PID file by default, it should be
	explicitely set with --pidfile.

	No longer log to syslog if in foreground. Logs are
	instead output to stderr.

	The four changes above make it straightforward to
	integrate sslh with systemd, and should help with
	launchd.
2013-07-10 23:14:48 +02:00
Yves Rutschle ae008179f0 v1.10:
Fixed calls referring to sockaddr length so they work
	with FreeBSD.

	Try target addresses in turn until one works if
	there are several (e.g. "localhost:22" resolves to
	an IPv6 address and an IPv4 address and sshd does
	not listen on IPv6).

	Fixed sslh-fork so killing the head process kills
	the listener processes.

	Heavily cleaned up test suite. Added stress test
	t_load script. Added coverage (requires lcov).

	Support for XMPP (Arnaud Gendre).

	Updated README.MacOSX (Aaron Madlon-Kay).
2013-07-10 23:14:15 +02:00
Yves Rutschle a9c9941988 v1.9: 02AUG2011
WARNING: Options changed, you'll need to update your
	start-up scripts! Log format changed, you'll need to
	update log processing scripts!

	Now supports IPv6 throughout (both on listening and
	forwarding)

	Logs now contain IPv6 addresses, local forwarding
	address, and resolves names (unless --numeric is
	specified).

	Introduced long options.

	Options -l, -s and -o replaced by their long
	counterparts.

	Defaults for SSL and SSH options suppressed (it's
	legitimate to want to use sslh to mux OpenVPN and
	tinc while not caring about SSH nor SSL).

	Bind to multiple addresses with multiple -p options.

	Support for tinc VPN (experimental).

	Numeric logging option.
2013-07-10 23:13:32 +02:00
Yves Rutschle 80f76c6fc5 v1.8:
Changed log format to make it possible to link
	connections to subsequent logs from other services.

	Updated CentOS init.d script (Andre Krajnik).

	Fixed zombie issue with OpenBSD (The SA_NOCLDWAIT flag is not
	propagated to the child process, so we set up signals after
	the fork.) (François FRITZ)

	Added -o "OpenVPN" and OpenVPN probing and support.

	Added single-threaded, select(2)-based version.

	Added support for "Bold" SSH clients (clients that speak first)
	Thanks to Guillaume Ricaud for spotting a regression
	bug.

	Added -f "foreground" option.

	Added test suite. (only tests connexions. No test for libwrap,
	setsid, setuid and so on) and corresponding 'make
	test' target.

	Added README.MacOSX (thanks Aaron Madlon-Kay)

	Documented use with proxytunnel and corkscrew in
	README.
2013-07-10 23:12:42 +02:00
Yves Rutschle 44f02ddf39 v1.7: 01FEB2010
Added CentOS init.d script (Andre Krajnik).

	Fixed default ssl address inconsistancy, now
	defaults to "localhost:443" and fixed documentation
	accordingly (pointed by Markus Schalke).

	Children no longer bind to the listen socket, so
	parent server can be stopped without killing an
	active child (pointed by Matthias Buecher).

	Inetd support (Dima Barsky).
2013-07-10 23:11:40 +02:00
Yves Rutschle 0658982705 v1.6: 25APR2009
Added -V, version option.
        Install target directory configurable in Makefile
        Changed syslog prefix in auth.log to "sslh[%pid]"
        Man page
        new 'make install' and 'make install-debian' targets
        PID file now specified using -P command line option
        Actually fixed zombie generation (the v1.5 patch got
        lost, doh!)
2013-07-10 23:10:43 +02:00
107 changed files with 21267 additions and 575 deletions

No files matched your search

+63
View File
@@ -0,0 +1,63 @@
name: Create and publish Container image
on:
push:
branches:
- master
tags:
- 'v*'
pull_request:
branches:
- master
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push-image:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to Container registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Docker meta
id: meta
uses: docker/metadata-action@v4
with:
images: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=edge
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
- name: Build and push
uses: docker/build-push-action@v4
with:
platforms: linux/amd64,linux/386,linux/arm64,linux/arm/v6,linux/arm/v7
context: .
file: Dockerfile
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
+10
View File
@@ -0,0 +1,10 @@
*.swp
*.swo
*.o
cscope.*
echosrv
sslh-fork
sslh-select
sslh.8.gz
tags
version.h
+339
View File
@@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
+454
View File
@@ -0,0 +1,454 @@
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
protocols), but based on libev, which should make it
scalable to large numbers of connections.
New log system: instead of --verbose with arbitrary
levels, there are now several message classes. Each
message class can be set to go to stderr, syslog, or
both. Classes are documented in example.cfg.
UDP connections are now managed in a hash to avoid
linear searches. The downside is that the number of
UDP connections is a hard limit, configurable with
the 'udp_max_connections', which defaults to 1024.
Timeouts are managed with lists.
inetd merges stderr output to what is sent to the
client, which is a security issue as it might give
information to an attacker. When inetd is activated,
stderr is forcibly closed.
New protocol-level option `resolve_on_forward`,
requests that target names are resolved at each
connection instead of at startup. Useful for dynamic
DNS situations. (Paul Schroeder/milkpirate)
New probe for MSRDP (akappner).
v1.22: 17AUG2021
sslh-select now supports UDP protocols.
Probes specified in the `protocols`
configuration entry are tried on incoming packets,
TCP or UDP, and forwarded based on the input
protocol (an incoming TCP connection will be
forwarded as TCP, and same with UDP).
This has been tested with DNS as shown in udp.cfg:
incoming packets that contain my domain name are
assumed to be a DNS request and forwarded
accordingly. Note this could cause problems if
combined with incoming TLS with SNI. UDP clients
and servers need to agree on the IPv4/IPv6 they use:
use the same protocol on all sides! Often, this
means explicitly using 'ip4-localhost'.
UDP sender-receiver pairs (connections, so to speak)
are kept for 60s, which can be changed with
`udp_timeout` in the configuration.
Added probes for UDP protocols QUICK and Teamspeak.
Added probes for syslog protocol.
sslh-select refactored to change linear searches
through connections to linear searches through
fd_set.
Fixed a libconfig call to support libconfig 1.7.3.
Added symbol to support libconfig 1.4.9, still in
use in CentOS7.
Warn about unknown settings in the configuration
file.
Added per-protocol `transparent` option. sslh-fork
drops the capability after creating the server-side
transparent socket. Transparent now uses CAP_NET_RAW
instead of CAP_NET_ADMIN.
Removed compile-time option to use POSIX regex. Now
regex must be PCRE2 (Perl-Compatible). This was in
fact the case since v1.21, as PCRE are used to parse
the config file.
v1.21: 11JUL2020
WARNING:
Moved configuration and command-line management to
use conf2struct. Changes are:
* `--ssl` and using `name: 'ssl'` in config file is no longer supported, use `tls` instead.
* command line option <-F|--config> no longer defaults to /etc/sslh.cfg, so you have to
specify it explicitly.
* command line option <-v|--verbose> takes a mandatory integer parameter
Added TCP_FASTOPEN support for client sockets (if
tfo_ok is specified in their configuration) and for
listening socket, if all client protocols support it.
(Craig Andrews)
Added 'minlength' option to skip a probe if less
than that many bytes have been received (mostly for
regex)
Update Let's Encrypt entry in example.cfg for tls-alpn-01
challenges; tls-sni-* challenges are now deprecated.
Log to syslog even if in foreground (for people who
use fail2ban)
Use syslog_facility: "none" to disable syslog
output.
Changed exit code for illegal command line parameter
from 1 to 6 (for testing purposes)
v1.20: 20NOV2018
Added support for socks5 protocol (Eugene Protozanov)
New probing method:
Before, probes were tried in order, repeating on the
same probe as long it returned PROBE_AGAIN before
moving to the next one. This means a probe which
requires a lot of data (i.e. return PROBE_AGAIN for
a long time) could prevent successful matches from
subsequent probes. The configuration file needed to
take that into account.
Now, all probes are tried each time new data is
found. If any probe matches, use it. If at least one
probe requires more data, wait for more. If all
probes failed, connect to the last one. So the only
thing to know when writing the configuration file is
that 'anyprot' needs to be last.
Test suite heavily refactored; `t` uses `test.cfg`
to decide which probes to test and all setup is
automatic; probes get tested with 'fast' (entire
first message in one packet) and 'slow' (one byte at
a time); when SNI/ALPN are defined, all combinations
are tested.
Old 'tls' probe removed, 'sni_alpn' probe renamed as 'tls'.
You'll need to change 'sni_alpn' to 'tls' in
your configuration file, if ever you used it.
v1.19: 20JAN2018
Added 'syslog_facility' configuration option to
specify where to log.
TLS now supports SNI and ALPN (Travis Burtrum),
including support for Let's Encrypt challenges
(Jonathan McCrohan)
ADB probe. (Mike Frysinger)
Added per-protocol 'fork' option. (Oleg Oshmyan)
Added chroot option. (Mike Frysinger)
A truckload of bug fixes and documentation
improvements (Various contributors)
v1.18: 29MAR2016
Added USELIBPCRE to make use of regex engine
optional.
Added support for RFC4366 SNI and RFC7301 ALPN
(Travis Burtrum)
Changed connection log to include the name of the probe that
triggered.
Changed configuration file format: 'probe' field is
no longer required, 'name' field can now contain
'tls' or 'regex', with corresponding options (see
example.cfg)
Added 'log_level' option to each protocol, which
allows to turn off generation of log at each
connection.
Added 'keepalive' option.
v1.17: 09MAR2015
Support RFC5952-style IPv6 addresses, e.g. [::]:443.
Transparent proxy support for FreeBSD.
(Ruben van Staveren)
Using -F with no argument will try
/etc/sslh/sslh.cfg and then /etc/sslh.cfg as
configuration files. (argument to -F can no longer
be separated from the option by a space, e.g. must
be -Ffoo.cfg)
Call setgroups() before setgid() (fixes potential
privilege escalation).
(Lars Vogdt)
Use portable way of getting modified time for OSX
support.
(Aaron Madlon-Kay)
Example configuration for fail2ban.
(Every Mouw)
v1.16: 11FEB2014
Probes made more resilient, to incoming data
containing NULLs. Also made them behave properly
when receiving too short packets to probe on the
first incoming packet.
(Ondrej Kuzník)
Libcap support: Keep only CAP_NET_ADMIN if started
as root with transparent proxying and dropping
privileges (enable USELIBCAP in Makefile). This
avoids having to mess with filesystem capabilities.
(Sebastian Schmidt/yath)
Fixed bugs related to getpeername that would cause
sslh to quit erroneously (getpeername can return
actual errors if connections are dropped before
getting to getpeername).
Set IP_FREEBIND if available to bind to addresses
that don't yet exist.
v1.15: 27JUL2013
Added --transparent option for transparent proxying.
See README for iptables magic and capability
management.
Fixed bug in sslh-select: if number of opened file
descriptor became bigger than FD_SETSIZE, bad things
would happen.
Fixed bug in sslh-select: if socket dropped while
deferred_data was present, sslh-select would crash.
Increased FD_SETSIZE for Cygwin, as the default 64
is too low for even moderate load.
v1.14: 21DEC2012
Corrected OpenVPN probe to support pre-shared secret
mode (OpenVPN port-sharing code is... wrong). Thanks
to Kai Ellinger for help in investigating and
testing.
Added an actual TLS/SSL probe.
Added configurable --on-timeout protocol
specification.
Added a --anyprot protocol probe (equivalent to what
--ssl was).
Makefile respects the user's compiler and CFLAG
choices (falling back to the current values if
undefined), as well as LDFLAGS.
(Michael Palimaka)
Added "After" and "KillMode" to systemd.sslh.service
(Thomas Weißschuh).
Added LSB tags to etc.init.d.sslh
(Thomas Varis).
v1.13: 18MAY2012
Write PID file before dropping privileges.
Added --background, which overrides 'foreground'
configuration file setting.
Added example systemd service file from Archlinux in
scripts/
https://projects.archlinux.org/svntogit/community.git/tree/trunk/sslh.service?h=packages/sslh
(Sébastien Luttringer)
v1.12: 08MAY2012
Added support for configuration file.
New protocol probes can be defined using regular
expressions that match the first packet sent by the
client.
sslh now connects timed out connections to the first
configured protocol instead of 'ssh' (just make sure
ssh is the first defined protocol).
sslh now tries protocols in the order in which they
are defined (just make sure sslh is the last defined
protocol).
v1.11: 21APR2012
WARNING: defaults have been removed for --user and
--pidfile options, update your start-up scripts!
No longer stop sslh when reverse DNS requests fail
for logging.
Added HTTP probe.
No longer create new session if running in
foreground.
No longer default to changing user to 'nobody'. If
--user isn't specified, just run as current user.
No longer create PID file by default, it should be
explicitly set with --pidfile.
No longer log to syslog if in foreground. Logs are
instead output to stderr.
The four changes above make it straightforward to
integrate sslh with systemd, and should help with
launchd.
v1.10: 27NOV2011
Fixed calls referring to sockaddr length so they work
with FreeBSD.
Try target addresses in turn until one works if
there are several (e.g. "localhost:22" resolves to
an IPv6 address and an IPv4 address and sshd does
not listen on IPv6).
Fixed sslh-fork so killing the head process kills
the listener processes.
Heavily cleaned up test suite. Added stress test
t_load script. Added coverage (requires lcov).
Support for XMPP (Arnaud Gendre).
Updated README.MacOSX (Aaron Madlon-Kay).
v1.9: 02AUG2011
WARNING: This version does not work with FreeBSD and
derivatives!
WARNING: Options changed, you'll need to update your
start-up scripts! Log format changed, you'll need to
update log processing scripts!
Now supports IPv6 throughout (both on listening and
forwarding)
Logs now contain IPv6 addresses, local forwarding
address, and resolves names (unless --numeric is
specified).
Introduced long options.
Options -l, -s and -o replaced by their long
counterparts.
Defaults for SSL and SSH options suppressed (it's
legitimate to want to use sslh to mux OpenVPN and
tinc while not caring about SSH nor SSL).
Bind to multiple addresses with multiple -p options.
Support for tinc VPN (experimental).
Numeric logging option.
v1.8: 15JUL2011
Changed log format to make it possible to link
connections to subsequent logs from other services.
Updated CentOS init.d script (Andre Krajnik).
Fixed zombie issue with OpenBSD (The SA_NOCLDWAIT flag is not
propagated to the child process, so we set up signals after
the fork.) (François FRITZ)
Added -o "OpenVPN" and OpenVPN probing and support.
Added single-threaded, select(2)-based version.
Added support for "Bold" SSH clients (clients that speak first)
Thanks to Guillaume Ricaud for spotting a regression
bug.
Added -f "foreground" option.
Added test suite. (only tests connexions. No test for libwrap,
setsid, setuid and so on) and corresponding 'make
test' target.
Added README.MacOSX (thanks Aaron Madlon-Kay)
Documented use with proxytunnel and corkscrew in
README.
v1.7: 01FEB2010
Added CentOS init.d script (Andre Krajnik).
Fixed default ssl address inconsistency, now
defaults to "localhost:443" and fixed documentation
accordingly (pointed by Markus Schalke).
Children no longer bind to the listen socket, so
parent server can be stopped without killing an
active child (pointed by Matthias Buecher).
Inetd support (Dima Barsky).
v1.6: 25APR2009
Added -V, version option.
Install target directory configurable in Makefile
Changed syslog prefix in auth.log to "sslh[%pid]"
Man page
new 'make install' and 'make install-debian' targets
PID file now specified using -P command line option
Actually fixed zombie generation (the v1.5 patch got
lost, doh!)
v1.5: 10DEC2008
Fixed zombie generation.
Added support scripts (), Makefile.
Changed all 'connexions' to 'connections' to please
pesky users. Damn users.
v1.4: 13JUL2008
Added libwrap support for ssh service (Christian Weinberger)
Only SSH is libwraped, not SSL.
v1.3: 14MAY2008
Added parsing for local interface to listen on
Changed default SSL connection to port 442 (443 doesn't make
sense as a default as we're already listening on 443)
Syslog incoming connections
v1.2: 12MAY2008
Fixed compilation warning for AMD64 (Thx Daniel Lange)
v1.1: 21MAY2007
Making sslhc more like a real daemon:
* If $PIDFILE is defined, write first PID to it upon startup
* Fork at startup (detach from terminal)
(thanks to http://www.enderunix.org/docs/eng/daemon.php -- good checklist)
* Less memory usage (?)
v1.0:
Basic functionality: privilege dropping, target hostnames and ports
configurable.
+38
View File
@@ -0,0 +1,38 @@
ARG ALPINE_VERSION="latest"
ARG TARGET_ARCH="library"
FROM docker.io/${TARGET_ARCH}/alpine:${ALPINE_VERSION} AS build
WORKDIR /sslh
RUN apk add --no-cache \
'gcc' \
'libconfig-dev' \
'make' \
'musl-dev' \
'pcre2-dev' \
'perl' \
;
COPY . /sslh
RUN make sslh-select && strip sslh-select
FROM docker.io/${TARGET_ARCH}/alpine:${ALPINE_VERSION}
COPY --from=build "/sslh/sslh-select" "/usr/local/bin/sslh"
RUN apk add --no-cache \
'libconfig' \
'pcre2' \
'iptables' \
'ip6tables' \
'libcap' \
&& \
adduser -s '/bin/sh' -S -D sslh && \
setcap cap_net_bind_service,cap_net_raw+ep /usr/local/bin/sslh
COPY "./container-entrypoint.sh" "/init"
ENTRYPOINT [ "/init" ]
# required for updating iptables
USER root:root
+152 -9
View File
@@ -1,23 +1,166 @@
# Configuration
USELIBWRAP=1 # Use libwrap?
VERSION=$(shell ./genver.sh -r)
# Configuration -- you probably need to `make clean` if you
# change any of these
ENABLE_SANITIZER= # Enable ASAN/LSAN/UBSAN
ENABLE_REGEX=1 # Enable regex probes
USELIBCONFIG=1 # Use libconfig? (necessary to use configuration files)
USELIBWRAP?= # Use libwrap?
USELIBCAP= # Use libcap?
USESYSTEMD= # Make use of systemd socket activation
USELIBBSD?= # Use libbsd (needed to update process name in `ps`)
COV_TEST= # Perform test coverage?
PREFIX?=/usr
BINDIR?=$(PREFIX)/sbin
MANDIR?=$(PREFIX)/share/man/man8
MAN=sslh.8.gz # man page name
# End of configuration -- the rest should take care of
# itself
CC = gcc
ifneq ($(strip $(ENABLE_SANITIZER)),)
CFLAGS_SAN=-fsanitize=address -fsanitize=leak -fsanitize=undefined
endif
#LIBS=-lnet
LIBS=
ifneq ($(strip $(COV_TEST)),)
CFLAGS_COV=-fprofile-arcs -ftest-coverage
endif
CC ?= gcc
AR ?= ar
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_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)
EV_OBJS=processes.o udp-listener.o sslh-ev.o hash.o tcp-listener.o $(OBJS_A)
CONDITIONAL_TARGETS=
ifneq ($(strip $(USELIBWRAP)),)
LIBS:=$(LIBS) -lwrap
CFLAGS=-DLIBWRAP
CPPFLAGS+=-DLIBWRAP
endif
all:
$(CC) $(CFLAGS) -o sslh sslh.c $(LIBS)
strip sslh
ifneq ($(strip $(ENABLE_REGEX)),)
CPPFLAGS+=-DENABLE_REGEX
endif
ifneq ($(strip $(USELIBCONFIG)),)
LIBS:=$(LIBS) -lconfig
CPPFLAGS+=-DLIBCONFIG
endif
ifneq ($(strip $(USELIBCAP)),)
LIBS:=$(LIBS) -lcap
CPPFLAGS+=-DLIBCAP
endif
ifneq ($(strip $(USESYSTEMD)),)
LIBS:=$(LIBS) -lsystemd
CPPFLAGS+=-DSYSTEMD
CONDITIONAL_TARGETS+=systemd-sslh-generator
endif
ifneq ($(strip $(USELIBBSD)),)
LIBS:=$(LIBS) -lbsd
CPPFLAGS+=-DLIBBSD
endif
all: sslh $(MAN) echosrv $(CONDITIONAL_TARGETS)
%.o: %.c %.h version.h
$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@
$(OBJS_A): $(OBJS)
$(AR) rcs $(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
c2s:
conf2struct sslhconf.cfg
conf2struct echosrv.cfg
sslh-conf.c sslh-conf.h: sslhconf.cfg
$(warning "sslhconf.cfg is more recent than sslh-conf.[ch]. Use `make c2s` to rebuild using `conf2struct`")
sslh-fork: version.h Makefile $(FORK_OBJS)
$(CC) $(CFLAGS) $(LDFLAGS) -o sslh-fork $(FORK_OBJS) $(LIBS)
sslh-select: version.h $(SELECT_OBJS) Makefile
$(CC) $(CFLAGS) $(LDFLAGS) -o sslh-select $(SELECT_OBJS) $(LIBS)
sslh-ev: version.h $(EV_OBJS) Makefile
$(CC) $(CFLAGS) $(LDFLAGS) -o sslh-ev $(EV_OBJS) $(LIBS) -lev
systemd-sslh-generator: systemd-sslh-generator.o
$(CC) $(CFLAGS) $(LDFLAGS) -o systemd-sslh-generator systemd-sslh-generator.o -lconfig
echosrv-conf.c echosrv-conf.h: echosrv.cfg
$(warning "echosrv.cfg is more recent than echosrv-conf.[ch]. Use `make c2s` to rebuild using `conf2struct`")
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)
$(MAN): sslh.pod Makefile
pod2man --section=8 --release=$(VERSION) --center=" " sslh.pod | gzip -9 - > $(MAN)
# Create release: export clean tree and tag current
# configuration
release:
git archive master --prefix="sslh-$(VERSION)/" | gzip > /tmp/sslh-$(VERSION).tar.gz
gpg --detach-sign --armor /tmp/sslh-$(VERSION).tar.gz
# Build docker image
docker:
docker image build -t "sslh:${VERSION}" .
docker image tag "sslh:${VERSION}" sslh:latest
docker-clean:
yes | docker image rm "sslh:${VERSION}" sslh:latest
yes | docker image prune
# generic install: install binary and man page
install: sslh $(MAN)
mkdir -p $(DESTDIR)/$(BINDIR)
mkdir -p $(DESTDIR)/$(MANDIR)
install -p sslh-fork $(DESTDIR)/$(BINDIR)/sslh
install -p -m 0644 $(MAN) $(DESTDIR)/$(MANDIR)/$(MAN)
# "extended" install for Debian: install startup script
install-debian: install sslh $(MAN)
sed -e "s+^PREFIX=+PREFIX=$(PREFIX)+" scripts/etc.init.d.sslh > /etc/init.d/sslh
chmod 755 /etc/init.d/sslh
update-rc.d sslh defaults
uninstall:
rm -f $(DESTDIR)$(BINDIR)/sslh $(DESTDIR)$(MANDIR)/$(MAN) $(DESTDIR)/etc/init.d/sslh $(DESTDIR)/etc/default/sslh
update-rc.d sslh remove
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
tags:
ctags --globals -T *.[ch]
cscope:
-find . -name "*.[chS]" >cscope.files
-cscope -b -R
test:
./t
-83
View File
@@ -1,83 +0,0 @@
sslh -- A ssl/ssh multiplexer.
sslh lets one accept both HTTPS and SSH connections on the
same port. It makes it possible to connect to an SSH server
on port 443 (e.g. from inside a corporate firewall) while
still serving HTTPS on that port.
Compilation instructions:
Solaris:
cc -o sslh sslh.c -lresolv -lsocket -lnsl
LynxOS:
gcc -o tcproxy tcproxy.c -lnetinet
Linux:
cc -o sslh sslh.c -lnet
or:
cc -o sslh sslh.c
To compile with libwrap support:
cc -o sslh -DLIBWRAP sslh.c -lwrap
To install:
make
cp sslh /usr/local/sbin
cp scripts/etc.init.d.sslh /etc/init.d/sslh
cp scripts/etc.default.sslh /etc/default/sslh
You can edit settings in /etc/default/sslh:
PIDFILE=/var/run/sslh.pid
LISTEN=ifname:443
SSH=localhost:22
SSL=localhost:443
A good scheme is to use the external name of the machine in
$LISTEN, and bind httpd to localhost:443: that way, https
connections coming from inside your network don't need to go
through sslh, and sslh is only there as a frontal for
connections coming from the internet.
Sslh can optionnaly perform libwrap checks for the sshd
service: because the connection to sshd will be coming
locally from sslh, sshd cannot determine the IP of the
client.
Comments? questions? sslh@rutschle.net
HISTORY
v1.5: 10DEC2008
Fixed zombie generation.
Added support scripts (), Makefile.
Changed all 'connexions' to 'connections' to please
pesky users. Damn users.
v1.4: 13JUL2008
Added libwrap support for ssh service (Christian Weinberger)
Only SSH is libwraped, not SSL.
v1.3: 14MAY2008
Added parsing for local interface to listen on
Changed default SSL connection to port 442 (443 doesn't make
sense as a default as we're already listening on 443)
Syslog incoming connections
v1.2: 12MAY2008
Fixed compilation warning for AMD64 (Thx Daniel Lange)
v1.1: 21MAY2007
Making sslhc more like a real daemon:
* If $PIDFILE is defined, write first PID to it upon startup
* Fork at startup (detach from terminal)
(thanks to http://www.enderunix.org/docs/eng/daemon.php -- good checklist)
* Less memory usage (?)
v1.0:
* Basic functionality: privilege dropping, target hostnames and ports
configurable.
+173
View File
@@ -0,0 +1,173 @@
sslh -- A ssl/ssh multiplexer
=============================
`sslh` accepts connections on specified ports, and forwards
them further based on tests performed on the first data
packet sent by the remote client.
Probes for HTTP, TLS/SSL (including SNI and ALPN), SSH,
OpenVPN, tinc, XMPP, SOCKS5, are implemented, and any other
protocol that can be tested using a regular expression, can
be recognised. A typical use case is to allow serving
several services on port 443 (e.g. to connect to SSH from
inside a corporate firewall, which almost never block port
443) while still serving HTTPS on that port.
Hence `sslh` acts as a protocol demultiplexer, or a
switchboard. With the SNI and ALPN probe, it makes a good
front-end to a virtual host farm hosted behind a single IP
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.
Install
=======
Please refer to the [install guide](doc/INSTALL.md).
Configuration
=============
Please refer to the [configuration guide](doc/config.md).
Docker image
------------
How to use
---
```bash
docker run \
--cap-add CAP_NET_RAW \
--cap-add CAP_NET_BIND_SERVICE \
--rm \
-it \
ghcr.io/yrutschle/sslh:latest \
--foreground \
--listen=0.0.0.0:443 \
--ssh=hostname:22 \
--tls=hostname:443
```
docker-compose example
```yaml
version: "3"
services:
sslh:
image: sslh:latest
hostname: sslh
ports:
- 443:443
command: --foreground --listen=0.0.0.0:443 --tls=nginx:443 --openvpn=openvpn:1194
depends_on:
- nginx
- openvpn
nginx:
image: nginx
openvpn:
image: openvpn
```
Transparent mode 1: using sslh container for networking
_Note: For transparent mode to work, the sslh container must be able to reach your services via **localhost**_
```yaml
version: "3"
services:
sslh:
build: https://github.com/yrutschle/sslh.git
container_name: sslh
environment:
- TZ=${TZ}
cap_add:
- NET_ADMIN
- NET_RAW
- NET_BIND_SERVICE
sysctls:
- net.ipv4.conf.default.route_localnet=1
- net.ipv4.conf.all.route_localnet=1
command: --transparent --foreground --listen=0.0.0.0:443 --tls=localhost:8443 --openvpn=localhost:1194
ports:
- 443:443 #sslh
- 80:80 #nginx
- 8443:8443 #nginx
- 1194:1194 #openvpn
extra_hosts:
- localbox:host-gateway
restart: unless-stopped
nginx:
image: nginx:latest
.....
network_mode: service:sslh #set nginx container to use sslh networking.
# ^^^ This is required. This makes nginx reachable by sslh via localhost
openvpn:
image: openvpn:latest
.....
network_mode: service:sslh #set openvpn container to use sslh networking
```
Transparent mode 2: using host networking
```yaml
version: "3"
services:
sslh:
build: https://github.com/yrutschle/sslh.git
container_name: sslh
environment:
- TZ=${TZ}
cap_add:
- NET_ADMIN
- NET_RAW
- NET_BIND_SERVICE
# must be set manually
#sysctls:
# - net.ipv4.conf.default.route_localnet=1
# - net.ipv4.conf.all.route_localnet=1
command: --transparent --foreground --listen=0.0.0.0:443 --tls=localhost:8443 --openvpn=localhost:1194
network_mode: host
restart: unless-stopped
nginx:
image: nginx:latest
.....
ports:
- 8443:8443 # bind to docker host on port 8443
openvpn:
image: openvpn:latest
.....
ports:
- 1194:1194 # bind to docker host on port 1194
```
Comments? Questions?
====================
You can subscribe to the `sslh` mailing list here:
<https://lists.rutschle.net/mailman/listinfo/sslh>
This mailing list should be used for discussion, feature
requests, and will be the preferred channel for announcements.
Of course, check the [FAQ](doc/FAQ.md) first!
+25
View File
@@ -0,0 +1,25 @@
Here's a list of features that have been suggested or
sometimes requested. This list is not a roadmap and
shouldn't be construed to mean that any of this will happen.
- configurable behaviour depending on services (e.g.
select() for ssl but fork() for ssh).
- have certain services available only from specified subnets
- some sort of "service knocking" allowing to activate a
service upon some external even, similar to port knocking;
for example, go to a specific URL to enable sslh forwarding
to sshd for a set period of time:
* sslh listens on 443 and only directs to httpd
* user goes somewhere to https://example.org/open_ssh.cgi
* open_ssh.cgi tells sslh
* sslh starts checking if incoming connections are ssh, and
if they are, forward to sshd
* 10 minutes later, sslh stops forwarding to ssh
That would make it almost impossible for an observer
(someone who'd telnet regularly on 443) to ever notice both
services are available on 443.
+6006
View File
File diff suppressed because it is too large. Load diff
+273
View File
@@ -0,0 +1,273 @@
/*******************************************************************************
* argtable3: Declares the main interfaces of the library
*
* This file is part of the argtable3 library.
*
* Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann
* <sheitmann@users.sourceforge.net>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of STEWART HEITMANN nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
#ifndef ARGTABLE3
#define ARGTABLE3
#include <stdio.h> /* FILE */
#include <time.h> /* struct tm */
#ifdef __cplusplus
extern "C" {
#endif
#define ARG_REX_ICASE 1
#define ARG_DSTR_SIZE 200
#define ARG_CMD_NAME_LEN 100
#define ARG_CMD_DESCRIPTION_LEN 256
#ifndef ARG_REPLACE_GETOPT
#define ARG_REPLACE_GETOPT 1 /* use the embedded getopt as the system getopt(3) */
#endif /* ARG_REPLACE_GETOPT */
/* bit masks for arg_hdr.flag */
enum { ARG_TERMINATOR = 0x1, ARG_HASVALUE = 0x2, ARG_HASOPTVALUE = 0x4 };
#if defined(_WIN32)
#if defined(argtable3_EXPORTS)
#define ARG_EXTERN __declspec(dllexport)
#elif defined(argtable3_IMPORTS)
#define ARG_EXTERN __declspec(dllimport)
#else
#define ARG_EXTERN
#endif
#else
#define ARG_EXTERN
#endif
typedef struct _internal_arg_dstr* arg_dstr_t;
typedef void* arg_cmd_itr_t;
typedef void(arg_resetfn)(void* parent);
typedef int(arg_scanfn)(void* parent, const char* argval);
typedef int(arg_checkfn)(void* parent);
typedef void(arg_errorfn)(void* parent, arg_dstr_t ds, int error, const char* argval, const char* progname);
typedef void(arg_dstr_freefn)(char* buf);
typedef int(arg_cmdfn)(int argc, char* argv[], arg_dstr_t res);
typedef int(arg_comparefn)(const void* k1, const void* k2);
/*
* The arg_hdr struct defines properties that are common to all arg_xxx structs.
* The argtable library requires each arg_xxx struct to have an arg_hdr
* struct as its first data member.
* The argtable library functions then use this data to identify the
* properties of the command line option, such as its option tags,
* datatype string, and glossary strings, and so on.
* Moreover, the arg_hdr struct contains pointers to custom functions that
* are provided by each arg_xxx struct which perform the tasks of parsing
* that particular arg_xxx arguments, performing post-parse checks, and
* reporting errors.
* These functions are private to the individual arg_xxx source code
* and are the pointer to them are initialised by that arg_xxx struct's
* constructor function. The user could alter them after construction
* if desired, but the original intention is for them to be set by the
* constructor and left unaltered.
*/
typedef struct arg_hdr {
char flag; /* Modifier flags: ARG_TERMINATOR, ARG_HASVALUE. */
const char* shortopts; /* String defining the short options */
const char* longopts; /* String defining the long options */
const char* datatype; /* Description of the argument data type */
const char* glossary; /* Description of the option as shown by arg_print_glossary function */
int mincount; /* Minimum number of occurences of this option accepted */
int maxcount; /* Maximum number of occurences if this option accepted */
void* parent; /* Pointer to parent arg_xxx struct */
arg_resetfn* resetfn; /* Pointer to parent arg_xxx reset function */
arg_scanfn* scanfn; /* Pointer to parent arg_xxx scan function */
arg_checkfn* checkfn; /* Pointer to parent arg_xxx check function */
arg_errorfn* errorfn; /* Pointer to parent arg_xxx error function */
void* priv; /* Pointer to private header data for use by arg_xxx functions */
} arg_hdr_t;
typedef struct arg_rem {
struct arg_hdr hdr; /* The mandatory argtable header struct */
} arg_rem_t;
typedef struct arg_lit {
struct arg_hdr hdr; /* The mandatory argtable header struct */
int count; /* Number of matching command line args */
} arg_lit_t;
typedef struct arg_int {
struct arg_hdr hdr; /* The mandatory argtable header struct */
int count; /* Number of matching command line args */
int* ival; /* Array of parsed argument values */
} arg_int_t;
typedef struct arg_dbl {
struct arg_hdr hdr; /* The mandatory argtable header struct */
int count; /* Number of matching command line args */
double* dval; /* Array of parsed argument values */
} arg_dbl_t;
typedef struct arg_str {
struct arg_hdr hdr; /* The mandatory argtable header struct */
int count; /* Number of matching command line args */
const char** sval; /* Array of parsed argument values */
} arg_str_t;
typedef struct arg_rex {
struct arg_hdr hdr; /* The mandatory argtable header struct */
int count; /* Number of matching command line args */
const char** sval; /* Array of parsed argument values */
} arg_rex_t;
typedef struct arg_file {
struct arg_hdr hdr; /* The mandatory argtable header struct */
int count; /* Number of matching command line args*/
const char** filename; /* Array of parsed filenames (eg: /home/foo.bar) */
const char** basename; /* Array of parsed basenames (eg: foo.bar) */
const char** extension; /* Array of parsed extensions (eg: .bar) */
} arg_file_t;
typedef struct arg_date {
struct arg_hdr hdr; /* The mandatory argtable header struct */
const char* format; /* strptime format string used to parse the date */
int count; /* Number of matching command line args */
struct tm* tmval; /* Array of parsed time values */
} arg_date_t;
enum { ARG_ELIMIT = 1, ARG_EMALLOC, ARG_ENOMATCH, ARG_ELONGOPT, ARG_EMISSARG };
typedef struct arg_end {
struct arg_hdr hdr; /* The mandatory argtable header struct */
int count; /* Number of errors encountered */
int* error; /* Array of error codes */
void** parent; /* Array of pointers to offending arg_xxx struct */
const char** argval; /* Array of pointers to offending argv[] string */
} arg_end_t;
typedef struct arg_cmd_info {
char name[ARG_CMD_NAME_LEN];
char description[ARG_CMD_DESCRIPTION_LEN];
arg_cmdfn* proc;
} arg_cmd_info_t;
/**** arg_xxx constructor functions *********************************/
ARG_EXTERN struct arg_rem* arg_rem(const char* datatype, const char* glossary);
ARG_EXTERN struct arg_lit* arg_lit0(const char* shortopts, const char* longopts, const char* glossary);
ARG_EXTERN struct arg_lit* arg_lit1(const char* shortopts, const char* longopts, const char* glossary);
ARG_EXTERN struct arg_lit* arg_litn(const char* shortopts, const char* longopts, int mincount, int maxcount, const char* glossary);
ARG_EXTERN struct arg_int* arg_int0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary);
ARG_EXTERN struct arg_int* arg_int1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary);
ARG_EXTERN struct arg_int* arg_intn(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary);
ARG_EXTERN struct arg_dbl* arg_dbl0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary);
ARG_EXTERN struct arg_dbl* arg_dbl1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary);
ARG_EXTERN struct arg_dbl* arg_dbln(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary);
ARG_EXTERN struct arg_str* arg_str0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary);
ARG_EXTERN struct arg_str* arg_str1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary);
ARG_EXTERN struct arg_str* arg_strn(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary);
ARG_EXTERN struct arg_rex* arg_rex0(const char* shortopts, const char* longopts, const char* pattern, const char* datatype, int flags, const char* glossary);
ARG_EXTERN struct arg_rex* arg_rex1(const char* shortopts, const char* longopts, const char* pattern, const char* datatype, int flags, const char* glossary);
ARG_EXTERN struct arg_rex* arg_rexn(const char* shortopts,
const char* longopts,
const char* pattern,
const char* datatype,
int mincount,
int maxcount,
int flags,
const char* glossary);
ARG_EXTERN struct arg_file* arg_file0(const char* shortopts, const char* longopts, const char* datatype, const char* glossary);
ARG_EXTERN struct arg_file* arg_file1(const char* shortopts, const char* longopts, const char* datatype, const char* glossary);
ARG_EXTERN struct arg_file* arg_filen(const char* shortopts, const char* longopts, const char* datatype, int mincount, int maxcount, const char* glossary);
ARG_EXTERN struct arg_date* arg_date0(const char* shortopts, const char* longopts, const char* format, const char* datatype, const char* glossary);
ARG_EXTERN struct arg_date* arg_date1(const char* shortopts, const char* longopts, const char* format, const char* datatype, const char* glossary);
ARG_EXTERN struct arg_date* arg_daten(const char* shortopts, const char* longopts, const char* format, const char* datatype, int mincount, int maxcount, const char* glossary);
ARG_EXTERN struct arg_end* arg_end(int maxerrors);
#define ARG_DSTR_STATIC ((arg_dstr_freefn*)0)
#define ARG_DSTR_VOLATILE ((arg_dstr_freefn*)1)
#define ARG_DSTR_DYNAMIC ((arg_dstr_freefn*)3)
/**** other functions *******************************************/
ARG_EXTERN int arg_nullcheck(void** argtable);
ARG_EXTERN int arg_parse(int argc, char** argv, void** argtable);
ARG_EXTERN void arg_print_option(FILE* fp, const char* shortopts, const char* longopts, const char* datatype, const char* suffix);
ARG_EXTERN void arg_print_syntax(FILE* fp, void** argtable, const char* suffix);
ARG_EXTERN void arg_print_syntaxv(FILE* fp, void** argtable, const char* suffix);
ARG_EXTERN void arg_print_glossary(FILE* fp, void** argtable, const char* format);
ARG_EXTERN void arg_print_glossary_gnu(FILE* fp, void** argtable);
ARG_EXTERN void arg_print_errors(FILE* fp, struct arg_end* end, const char* progname);
ARG_EXTERN void arg_print_option_ds(arg_dstr_t ds, const char* shortopts, const char* longopts, const char* datatype, const char* suffix);
ARG_EXTERN void arg_print_syntax_ds(arg_dstr_t ds, void** argtable, const char* suffix);
ARG_EXTERN void arg_print_syntaxv_ds(arg_dstr_t ds, void** argtable, const char* suffix);
ARG_EXTERN void arg_print_glossary_ds(arg_dstr_t ds, void** argtable, const char* format);
ARG_EXTERN void arg_print_glossary_gnu_ds(arg_dstr_t ds, void** argtable);
ARG_EXTERN void arg_print_errors_ds(arg_dstr_t ds, struct arg_end* end, const char* progname);
ARG_EXTERN void arg_freetable(void** argtable, size_t n);
ARG_EXTERN arg_dstr_t arg_dstr_create(void);
ARG_EXTERN void arg_dstr_destroy(arg_dstr_t ds);
ARG_EXTERN void arg_dstr_reset(arg_dstr_t ds);
ARG_EXTERN void arg_dstr_free(arg_dstr_t ds);
ARG_EXTERN void arg_dstr_set(arg_dstr_t ds, char* str, arg_dstr_freefn* free_proc);
ARG_EXTERN void arg_dstr_cat(arg_dstr_t ds, const char* str);
ARG_EXTERN void arg_dstr_catc(arg_dstr_t ds, char c);
ARG_EXTERN void arg_dstr_catf(arg_dstr_t ds, const char* fmt, ...);
ARG_EXTERN char* arg_dstr_cstr(arg_dstr_t ds);
ARG_EXTERN void arg_cmd_init(void);
ARG_EXTERN void arg_cmd_uninit(void);
ARG_EXTERN void arg_cmd_register(const char* name, arg_cmdfn* proc, const char* description);
ARG_EXTERN void arg_cmd_unregister(const char* name);
ARG_EXTERN int arg_cmd_dispatch(const char* name, int argc, char* argv[], arg_dstr_t res);
ARG_EXTERN unsigned int arg_cmd_count(void);
ARG_EXTERN arg_cmd_info_t* arg_cmd_info(const char* name);
ARG_EXTERN arg_cmd_itr_t arg_cmd_itr_create(void);
ARG_EXTERN void arg_cmd_itr_destroy(arg_cmd_itr_t itr);
ARG_EXTERN int arg_cmd_itr_advance(arg_cmd_itr_t itr);
ARG_EXTERN char* arg_cmd_itr_key(arg_cmd_itr_t itr);
ARG_EXTERN arg_cmd_info_t* arg_cmd_itr_value(arg_cmd_itr_t itr);
ARG_EXTERN int arg_cmd_itr_search(arg_cmd_itr_t itr, void* k);
ARG_EXTERN void arg_mgsort(void* data, int size, int esize, int i, int k, arg_comparefn* comparefn);
ARG_EXTERN void arg_make_get_help_msg(arg_dstr_t res);
ARG_EXTERN void arg_make_help_msg(arg_dstr_t ds, char* cmd_name, void** argtable);
ARG_EXTERN void arg_make_syntax_err_msg(arg_dstr_t ds, void** argtable, struct arg_end* end);
ARG_EXTERN int arg_make_syntax_err_help_msg(arg_dstr_t ds, char* name, int help, int nerrors, void** argtable, struct arg_end* end, int* exitcode);
ARG_EXTERN void arg_set_module_name(const char* name);
ARG_EXTERN void arg_set_module_version(int major, int minor, int patch, const char* tag);
/**** deprecated functions, for back-compatibility only ********/
ARG_EXTERN void arg_free(void** argtable);
#ifdef __cplusplus
}
#endif
#endif
+32
View File
@@ -0,0 +1,32 @@
# This is a basic configuration file that should provide
# sensible values for "standard" setup.
# You will find extensive examples with explanations in
# example.cfg
timeout: 2;
user: "nobody";
pidfile: "/var/run/sslh.pid";
# Change hostname with your external address name, or the IP
# of the interface that receives connections
listen:
(
{ host: "thelonious"; port: "443"; }
);
# Change to the protocols you want to forward to. The
# defaults here are sensible for services running on
# localhost
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"; }
);
+98
View File
@@ -0,0 +1,98 @@
/*
collection.c: management of a collection of connections, for sslh-select
# Copyright (C) 2021 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 "common.h"
#include "collection.h"
#include "sslh-conf.h"
#include "gap.h"
/* Info to keep track of all connections */
struct cnx_collection {
gap_array* fd2cnx; /* Array indexed by file descriptor to things in cnx[] */
};
/* Allocates and initialises a new collection of connections with at least
* `len` elements. */
cnx_collection* collection_init(int len)
{
cnx_collection* collection;
collection = malloc(sizeof(*collection));
CHECK_ALLOC(collection, "collection_init(collection)");
memset(collection, 0, sizeof(*collection));
collection->fd2cnx = gap_init(len);
return collection;
}
/* Caveat: might not work, as has never been used */
void collection_destroy(cnx_collection* collection)
{
/* Caveat 2: no code to free connections yet */
gap_destroy(collection->fd2cnx);
free(collection);
}
/* Points the file descriptor to the specified connection index */
int collection_add_fd(cnx_collection* collection, struct connection* cnx, int fd)
{
gap_set(collection->fd2cnx, fd, cnx);
return 0;
}
/* Allocates a connection and inits it with specified file descriptor */
struct connection* collection_alloc_cnx_from_fd(struct cnx_collection* collection, int fd)
{
struct connection* cnx = malloc(sizeof(*cnx));
if (!cnx) return NULL;
init_cnx(cnx);
cnx->type = SOCK_STREAM;
cnx->q[0].fd = fd;
cnx->state = ST_PROBING;
cnx->probe_timeout = time(NULL) + cfg.timeout;
gap_set(collection->fd2cnx, fd, cnx);
return cnx;
}
/* Remove a connection from the collection */
int collection_remove_cnx(cnx_collection* collection, struct connection *cnx)
{
if (cnx->q[0].fd != -1)
gap_set(collection->fd2cnx, cnx->q[0].fd, NULL);
if (cnx->q[1].fd != -1)
gap_set(collection->fd2cnx, cnx->q[1].fd, NULL);
free(cnx);
return 0;
}
/* Returns the connection that contains the file descriptor */
struct connection* collection_get_cnx_from_fd(struct cnx_collection* collection, int fd)
{
return gap_get(collection->fd2cnx, fd);
}
+18
View File
@@ -0,0 +1,18 @@
#ifndef COLLECTION_H
#define COLLECTION_H
typedef struct cnx_collection cnx_collection;
cnx_collection* collection_init(int len);
void collection_destroy(cnx_collection* collection);
struct connection* collection_alloc_cnx_from_fd(cnx_collection* collection, int fd);
int collection_add_fd(cnx_collection* collection, struct connection* cnx, int fd);
/* Remove a connection from the collection */
int collection_remove_cnx(cnx_collection* collection, struct connection *cnx);
struct connection* collection_get_cnx_from_fd(struct cnx_collection* collection, int fd);
#endif
+851
View File
@@ -0,0 +1,851 @@
/* Code and variables that is common to both fork and select-based
* servers.
*
* No code here should assume whether sockets are blocking or not.
**/
#define _GNU_SOURCE
#include <stddef.h>
#include <stdarg.h>
#include <grp.h>
#include <sys/types.h>
#include <ifaddrs.h>
#include <netinet/in.h>
#include "common.h"
#include "probe.h"
#include "log.h"
#include "sslh-conf.h"
/* Added to make the code compilable under CYGWIN
* */
#ifndef SA_NOCLDWAIT
#define SA_NOCLDWAIT 0
#endif
/* Make use of systemd socket activation
* */
#ifdef SYSTEMD
#include <systemd/sd-daemon.h>
#endif
#ifdef LIBBSD
#include <bsd/unistd.h>
#endif
/*
* Settings that depend on the command line or the config file
*/
struct sslhcfg_item cfg;
struct addrinfo *addr_listen = NULL; /* what addresses do we listen to? */
#ifdef LIBWRAP
#include <tcpd.h>
int allow_severity =0, deny_severity = 0;
#endif
typedef enum {
CR_DIE,
CR_WARN
} CR_ACTION;
/* check result and die, printing the offending address and error */
void check_res_dump(CR_ACTION act, int res, struct addrinfo *addr, char* syscall)
{
char buf[NI_MAXHOST];
if (res == -1) {
print_message(msg_system_error, "%s:%s: %s\n",
sprintaddr(buf, sizeof(buf), addr),
syscall,
strerror(errno));
if (act == CR_DIE)
exit(1);
}
}
int get_fd_sockets(struct listen_endpoint *sockfd[])
{
int sd = 0;
#ifdef SYSTEMD
sd = sd_listen_fds(0);
if (sd < 0) {
print_message(msg_system_error, "sd_listen_fds(): %s\n", strerror(-sd));
exit(1);
}
if (sd > 0) {
int i;
*sockfd = malloc(sd * sizeof(*sockfd[0]));
CHECK_ALLOC(*sockfd, "malloc");
for (i = 0; i < sd; i++) {
(*sockfd)[i].socketfd = SD_LISTEN_FDS_START + i;
(*sockfd)[i].type = SOCK_STREAM;
}
}
#endif
return sd;
}
/* Set TCP_FASTOPEN on listening socket if all client protocols support it */
int make_listen_tfo(int s)
{
int i, qlen = 5;
/* Don't do it if not supported */
if (!TCP_FASTOPEN)
return 0;
/* Don't do it if any protocol does not specify it */
for (i = 0; i < cfg.protocols_len; i++) {
if (! cfg.protocols[i].tfo_ok)
return 0;
}
return setsockopt(s, SOL_SOCKET, TCP_FASTOPEN, (char*)&qlen, sizeof(qlen));
}
/* 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)
{
struct sockaddr_storage *saddr;
int sockfd, one, res;
saddr = (struct sockaddr_storage*)addr->ai_addr;
sockfd = socket(saddr->ss_family, udp ? SOCK_DGRAM : SOCK_STREAM, 0);
check_res_dump(CR_DIE, sockfd, addr, "socket");
one = 1;
res = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one));
check_res_dump(CR_DIE, res, addr, "setsockopt(SO_REUSEADDR)");
res = make_listen_tfo(sockfd);
check_res_dump(CR_WARN, res, addr, "setsockopt(TCP_FASTOPEN)");
if (keepalive) {
res = setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (char*)&one, sizeof(one));
check_res_dump(CR_DIE, res, addr, "setsockopt(SO_KEEPALIVE)");
}
if (IP_FREEBIND) {
res = setsockopt(sockfd, IPPROTO_IP, IP_FREEBIND, (char*)&one, sizeof(one));
check_res_dump(CR_WARN, res, addr, "setsockopt(IP_FREEBIND)");
}
if (addr->ai_addr->sa_family == AF_INET6) {
res = setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&one, sizeof(one));
check_res_dump(CR_WARN, res, addr, "setsockopt(IPV6_V6ONLY)");
}
res = bind(sockfd, addr->ai_addr, addr->ai_addrlen);
check_res_dump(CR_DIE, res, addr, "bind");
if (!udp) {
res = listen (sockfd, 50);
check_res_dump(CR_DIE, res, addr, "listen");
}
return sockfd;
}
/* Starts listening sockets on specified addresses.
* OUT: *sockfd[] pointer to newly-allocated array of listen_endpoint objects
* Returns number of addresses bound
*/
int start_listen_sockets(struct listen_endpoint *sockfd[])
{
struct addrinfo *addr, *start_addr;
char buf[NI_MAXHOST];
int i, res;
int num_addr = 0, keepalive = 0, udp = 0;
int sd_socks = 0;
sd_socks = get_fd_sockets(sockfd);
if (sd_socks > 0) {
return sd_socks;
}
*sockfd = NULL;
print_message(msg_config, "Listening to:\n");
for (i = 0; i < cfg.listen_len; i++) {
keepalive = cfg.listen[i].keepalive;
udp = cfg.listen[i].is_udp;
res = resolve_split_name(&start_addr, cfg.listen[i].host, cfg.listen[i].port);
if (res) exit(4);
for (addr = start_addr; addr; addr = addr->ai_next) {
num_addr++;
*sockfd = realloc(*sockfd, num_addr * sizeof(*sockfd[0]));
(*sockfd)[num_addr-1].socketfd = listen_single_addr(addr, keepalive, udp);
(*sockfd)[num_addr-1].type = udp ? SOCK_DGRAM : SOCK_STREAM;
print_message(msg_config, "%d:\t%s\t[%s] [%s]\n", (*sockfd)[num_addr-1].socketfd, sprintaddr(buf, sizeof(buf), addr),
cfg.listen[i].keepalive ? "keepalive" : "",
cfg.listen[i].is_udp ? "udp" : "");
}
freeaddrinfo(start_addr);
}
return num_addr;
}
/* returns 1 if given address is on the local machine: iterate through all
* network interfaces and check their addresses */
int is_same_machine(struct addrinfo* from)
{
struct ifaddrs *ifaddrs_p = NULL, *ifa;
int match = 0;
getifaddrs(&ifaddrs_p);
for (ifa = ifaddrs_p; ifa != NULL; ifa = ifa->ifa_next)
{
if (!ifa->ifa_addr)
continue;
if (from->ai_addr->sa_family == ifa->ifa_addr->sa_family)
{
int family = ifa->ifa_addr->sa_family;
if (family == AF_INET)
{
struct sockaddr_in *from_addr = (struct sockaddr_in*)from->ai_addr;
struct sockaddr_in *ifa_addr = (struct sockaddr_in*)ifa->ifa_addr;
if (from_addr->sin_addr.s_addr == ifa_addr->sin_addr.s_addr) {
match = 1;
break;
}
}
else if (family == AF_INET6)
{
struct sockaddr_in6 *from_addr = (struct sockaddr_in6*)from->ai_addr;
struct sockaddr_in6 *ifa_addr = (struct sockaddr_in6*)ifa->ifa_addr;
if (!memcmp(from_addr->sin6_addr.s6_addr, ifa_addr->sin6_addr.s6_addr, 16)) {
match = 1;
break;
}
}
}
}
freeifaddrs(ifaddrs_p);
return match;
}
/* Transparent proxying: bind the peer address of fd to the peer address of
* fd_from */
#define IP_TRANSPARENT 19
int bind_peer(int fd, int fd_from)
{
struct addrinfo from;
struct sockaddr_storage ss;
int res, trans = 1;
memset(&from, 0, sizeof(from));
from.ai_addr = (struct sockaddr*)&ss;
from.ai_addrlen = sizeof(ss);
/* getpeername can fail with ENOTCONN if connection was dropped before we
* got here */
res = getpeername(fd_from, from.ai_addr, &from.ai_addrlen);
CHECK_RES_RETURN(res, "getpeername", res);
/* 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");
#else
if (from.ai_addr->sa_family==AF_INET) { /* IPv4 */
res = setsockopt(fd, IPPROTO_IP, IP_BINDANY, &trans, sizeof(trans));
CHECK_RES_RETURN(res, "setsockopt IP_BINDANY", res);
#ifdef IPV6_BINDANY
} else { /* IPv6 */
res = setsockopt(fd, IPPROTO_IPV6, IPV6_BINDANY, &trans, sizeof(trans));
CHECK_RES_RETURN(res, "setsockopt IPV6_BINDANY", res);
#endif /* IPV6_BINDANY */
}
#endif /* IP_TRANSPARENT / IP_BINDANY */
res = bind(fd, from.ai_addr, from.ai_addrlen);
CHECK_RES_RETURN(res, "bind", res);
return 0;
}
/* Make the file descriptor non-block */
int set_nonblock(int fd)
{
int flags;
flags = fcntl(fd, F_GETFL);
CHECK_RES_RETURN(flags, "fcntl", -1);
flags |= O_NONBLOCK;
flags = fcntl(fd, F_SETFL, flags);
CHECK_RES_RETURN(flags, "fcntl", -1);
return flags;
}
/* Connect to first address that works and returns a file descriptor, or -1 if
* none work.
* If transparent proxying is on, use fd_from peer address on external address
* of new file descriptor. */
int connect_addr(struct connection *cnx, int fd_from, connect_blocking blocking)
{
struct addrinfo *a, from;
struct sockaddr_storage ss;
char buf[NI_MAXHOST];
int fd, res, one;
int transparent = cnx->proto->transparent || cfg.transparent;
memset(&from, 0, sizeof(from));
from.ai_addr = (struct sockaddr*)&ss;
from.ai_addrlen = sizeof(ss);
res = getpeername(fd_from, from.ai_addr, &from.ai_addrlen);
CHECK_RES_RETURN(res, "getpeername", res);
if (cnx->proto->resolve_on_forward) {
resolve_split_name(&(cnx->proto->saddr), cnx->proto->host,
cnx->proto->port);
}
for (a = cnx->proto->saddr; a; a = a->ai_next) {
/* When transparent, make sure both connections use the same address family */
if (transparent && a->ai_family != from.ai_addr->sa_family)
continue;
print_message(msg_connections_try, "trying to connect to %s family %d len %d\n",
sprintaddr(buf, sizeof(buf), a),
a->ai_addr->sa_family, a->ai_addrlen);
/* XXX Needs to match ai_family from fd_from when being transparent! */
fd = socket(a->ai_family, SOCK_STREAM, 0);
if (fd == -1) {
print_message(msg_connections_error, "forward to %s failed:socket: %s\n",
cnx->proto->name, strerror(errno));
} else {
one = 1;
setsockopt(fd, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, &one, sizeof(one));
/* no need to check return value; if it's not supported, that's okay */
if (blocking == NON_BLOCKING) {
set_nonblock(fd);
}
if (transparent) {
res = bind_peer(fd, fd_from);
if (res == -1) close(fd);
CHECK_RES_RETURN(res, "bind_peer", res);
}
res = connect(fd, a->ai_addr, a->ai_addrlen);
/* EINPROGRESS indicates it might take time. If it eventually
* fails, it'll be caught as a failed read */
if ((res == -1) && (errno != EINPROGRESS)) {
print_message(msg_connections_error, "forward to %s failed:connect: %s\n",
cnx->proto->name, strerror(errno));
close(fd);
continue; /* Try the next address */
}
if (cnx->proto->keepalive) {
res = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char*)&one, sizeof(one));
CHECK_RES_RETURN(res, "setsockopt(SO_KEEPALIVE)", res);
}
return fd;
}
}
return -1;
}
/* Store some data to write to the queue later */
int defer_write(struct queue *q, void* data, int data_size)
{
char *p;
ptrdiff_t data_offset = q->deferred_data - q->begin_deferred_data;
print_message(msg_fd, "writing deferred on fd %d\n", q->fd);
p = realloc(q->begin_deferred_data, data_offset + q->deferred_data_size + data_size);
CHECK_ALLOC(p, "realloc");
q->begin_deferred_data = p;
q->deferred_data = p + data_offset;
p += data_offset + q->deferred_data_size;
q->deferred_data_size += data_size;
memcpy(p, data, data_size);
return 0;
}
/* tries to flush some of the data for specified queue
* Upon success, the number of bytes written is returned.
* Upon failure, -1 returned (e.g. connexion closed)
* */
int flush_deferred(struct queue *q)
{
int 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;
if (n == q->deferred_data_size) {
/* All has been written -- release the memory */
free(q->begin_deferred_data);
q->begin_deferred_data = NULL;
q->deferred_data = NULL;
q->deferred_data_size = 0;
} else {
/* There is data left */
q->deferred_data += n;
q->deferred_data_size -= n;
}
return n;
}
void init_cnx(struct connection *cnx)
{
memset(cnx, 0, sizeof(*cnx));
cnx->q[0].fd = -1;
cnx->q[1].fd = -1;
cnx->proto = NULL;
}
void dump_connection(struct connection *cnx)
{
print_message(msg_int_error, "type: %s\n", cnx->type == SOCK_DGRAM ? "UDP" : "TCP");
print_message(msg_int_error, "state: %d\n", cnx->state);
print_message(msg_int_error, "0: fd %d, %d deferred\n", cnx->q[0].fd, cnx->q[0].deferred_data_size);
hexdump(msg_int_error, cnx->q[0].deferred_data, cnx->q[0].deferred_data_size);
print_message(msg_int_error, "1: fd %d, %d deferred\n", cnx->q[1].fd, cnx->q[1].deferred_data_size);
hexdump(msg_int_error, cnx->q[1].deferred_data, cnx->q[1].deferred_data_size);
}
/*
* moves data from one fd to other
*
* returns number of bytes copied if success
* returns 0 (FD_CNXCLOSED) if incoming socket closed
* returns FD_NODATA if no data was available
* returns FD_STALLED if data was read, could not be written, and has been
* stored in temporary buffer.
*/
int fd2fd(struct queue *target_q, struct queue *from_q)
{
char buffer[BUFSIZ];
int target, from, size_r, size_w;
target = target_q->fd;
from = from_q->fd;
size_r = read(from, buffer, sizeof(buffer));
if (size_r == -1) {
switch (errno) {
case EAGAIN:
return FD_NODATA;
case ECONNRESET:
case EPIPE:
return FD_CNXCLOSED;
}
}
CHECK_RES_RETURN(size_r, "read",FD_CNXCLOSED);
if (size_r == 0)
return FD_CNXCLOSED;
size_w = write(target, buffer, size_r);
/* process -1 when we know how to deal with it */
if (size_w == -1) {
switch (errno) {
case EAGAIN:
/* write blocked: Defer data */
defer_write(target_q, buffer, size_r);
return FD_STALLED;
case ECONNRESET:
case EPIPE:
/* remote end closed -- drop the connection */
return FD_CNXCLOSED;
}
} else if (size_w < size_r) {
/* incomplete write -- defer the rest of the data */
defer_write(target_q, buffer + size_w, size_r - size_w);
return FD_STALLED;
}
CHECK_RES_RETURN(size_w, "write", FD_CNXCLOSED);
return size_w;
}
/* returns a string that prints the IP and port of the sockaddr */
char* sprintaddr(char* buf, size_t size, struct addrinfo *a)
{
char host[NI_MAXHOST], serv[NI_MAXSERV];
int res;
res = getnameinfo(a->ai_addr, a->ai_addrlen,
host, sizeof(host),
serv, sizeof(serv),
cfg.numeric ? NI_NUMERICHOST | NI_NUMERICSERV : 0 );
if (res) {
print_message(msg_system_error, "sprintaddr:getnameinfo: %s\n", gai_strerror(res));
/* Name resolution failed: do it numerically instead */
res = getnameinfo(a->ai_addr, a->ai_addrlen,
host, sizeof(host),
serv, sizeof(serv),
NI_NUMERICHOST | NI_NUMERICSERV);
/* should not fail but... */
if (res) {
print_message(msg_system_error, "sprintaddr:getnameinfo(NUM): %s\n", gai_strerror(res));
strcpy(host, "?");
strcpy(serv, "?");
}
}
snprintf(buf, size, "%s:%s", host, serv);
return buf;
}
/* Turns a hostname and port (or service) into a list of struct addrinfo
* returns 0 on success, -1 otherwise and logs error
*/
int resolve_split_name(struct addrinfo **out, char* host, char* serv)
{
struct addrinfo hint;
char *end;
int res;
memset(&hint, 0, sizeof(hint));
hint.ai_family = PF_UNSPEC;
hint.ai_socktype = SOCK_STREAM;
/* If it is a RFC-Compliant IPv6 address ("[1234::12]:443"), remove brackets
* around IP address */
if (host[0] == '[') {
end = strrchr(host, ']');
if (!end) {
print_message(msg_config_error, "%s: no closing bracket in IPv6 address?\n", host);
return -1;
}
host++; /* skip first bracket */
*end = 0; /* remove last bracket */
}
res = getaddrinfo(host, serv, &hint, out);
if (res)
print_message(msg_system_error, "%s `%s:%s'\n", gai_strerror(res), host, 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)
{
int res;
struct addrinfo addr;
struct sockaddr_storage ss;
addr.ai_addr = (struct sockaddr*)&ss;
addr.ai_addrlen = sizeof(ss);
res = getpeername(cnx->q[0].fd, addr.ai_addr, &addr.ai_addrlen);
if (res == -1) return 0; /* Can happen if connection drops before we get here.
In that case, don't log anything (there is no connection) */
sprintaddr(desc->peer, sizeof(desc->peer), &addr);
addr.ai_addrlen = sizeof(ss);
res = getsockname(cnx->q[0].fd, addr.ai_addr, &addr.ai_addrlen);
if (res == -1) return 0;
sprintaddr(desc->service, sizeof(desc->service), &addr);
addr.ai_addrlen = sizeof(ss);
res = getpeername(cnx->q[1].fd, addr.ai_addr, &addr.ai_addrlen);
if (res == -1) return 0;
sprintaddr(desc->target, sizeof(desc->target), &addr);
addr.ai_addrlen = sizeof(ss);
res = getsockname(cnx->q[1].fd, addr.ai_addr, &addr.ai_addrlen);
if (res == -1) return 0;
sprintaddr(desc->local, sizeof(desc->local), &addr);
return 1;
}
void set_proctitle_shovel(struct connection_desc* desc, const struct connection *cnx)
{
#ifdef LIBBSD
struct connection_desc d;
if (!desc) {
desc = &d;
get_connection_desc(desc, cnx);
}
setproctitle("shovel %s %s->%s => %s->%s",
cnx->proto->name,
desc->peer,
desc->service,
desc->local,
desc->target);
#endif
}
/* libwrap (tcpd): check the connection is legal. This is necessary because
* the actual server will only see a connection coming from localhost and can't
* apply the rules itself.
*
* Returns -1 if access is denied, 0 otherwise
*/
int check_access_rights(int in_socket, const char* service)
{
#ifdef LIBWRAP
union {
struct sockaddr saddr;
struct sockaddr_storage ss;
} peer;
socklen_t size = sizeof(peer);
char addr_str[NI_MAXHOST], host[NI_MAXHOST];
int res;
res = getpeername(in_socket, &peer.saddr, &size);
CHECK_RES_RETURN(res, "getpeername", res);
/* extract peer address */
res = getnameinfo(&peer.saddr, size, addr_str, sizeof(addr_str), NULL, 0, NI_NUMERICHOST);
if (res) {
print_message(msg_system_error, "getnameinfo(NI_NUMERICHOST):%s\n", gai_strerror(res));
strcpy(addr_str, STRING_UNKNOWN);
}
/* extract peer name */
strcpy(host, STRING_UNKNOWN);
if (!cfg.numeric) {
res = getnameinfo(&peer.saddr, size, host, sizeof(host), NULL, 0, NI_NAMEREQD);
if (res) {
print_message(msg_system_error, "getnameinfo(NI_NAMEREQD):%s\n", gai_strerror(res));
}
}
if (!hosts_ctl(service, host, addr_str, STRING_UNKNOWN)) {
print_message(msg_connections, "connection from %s(%s): access denied", host, addr_str);
close(in_socket);
return -1;
}
#endif
return 0;
}
void setup_signals(void)
{
int res;
struct sigaction action;
/* Request no SIGCHLD is sent upon termination of
* the children */
memset(&action, 0, sizeof(action));
action.sa_handler = NULL;
action.sa_flags = SA_NOCLDWAIT;
res = sigaction(SIGCHLD, &action, NULL);
CHECK_RES_DIE(res, "sigaction");
/* Set SIGTERM to exit. For some reason if it's not set explicitly,
* coverage information is lost when killing the process */
memset(&action, 0, sizeof(action));
action.sa_handler = exit;
res = sigaction(SIGTERM, &action, NULL);
CHECK_RES_DIE(res, "sigaction");
/* Ignore SIGPIPE . */
action.sa_handler = SIG_IGN;
res = sigaction(SIGPIPE, &action, NULL);
CHECK_RES_DIE(res, "sigaction");
}
/* Ask OS to keep capabilities over a setuid(nonzero) */
void set_keepcaps(int val) {
#ifdef LIBCAP
int res;
res = prctl(PR_SET_KEEPCAPS, val, 0, 0, 0);
if (res) {
perror("prctl");
exit(1);
}
#endif
}
/* Returns true if anything requires transparent proxying. */
static int use_transparent(void)
{
#ifdef LIBCAP
if (cfg.transparent)
return 1;
for (int i = 0; i < cfg.protocols_len; i++)
if (cfg.protocols[i].transparent)
return 1;
#endif
return 0;
}
/* set needed capabilities for effective and permitted, clear rest
* IN: cap_net_admin: set to 1 to set CAP_NET_RAW
* */
void set_capabilities(int cap_net_admin) {
#ifdef LIBCAP
int res;
cap_t caps;
cap_value_t cap_list[10];
int ncap = 0;
if (cap_net_admin)
cap_list[ncap++] = CAP_NET_RAW;
caps = cap_init();
#define _cap_set_flag(flag) do { \
res = cap_clear_flag(caps, flag); \
CHECK_RES_DIE(res, "cap_clear_flag(" #flag ")"); \
if (ncap > 0) { \
res = cap_set_flag(caps, flag, ncap, cap_list, CAP_SET); \
CHECK_RES_DIE(res, "cap_set_flag(" #flag ")"); \
} \
} while(0)
_cap_set_flag(CAP_EFFECTIVE);
_cap_set_flag(CAP_PERMITTED);
#undef _cap_set_flag
res = cap_set_proc(caps);
CHECK_RES_DIE(res, "cap_set_proc");
res = cap_free(caps);
if (res) {
perror("cap_free");
exit(1);
}
#endif
}
/* We don't want to run as root -- drop privileges if required */
void drop_privileges(const char* user_name, const char* chroot_path)
{
int res;
struct passwd *pw = NULL;
if (user_name) {
pw = getpwnam(user_name);
if (!pw) {
print_message(msg_config_error, "%s: not found\n", user_name);
exit(2);
}
print_message(msg_config, "turning into %s\n", user_name);
}
if (chroot_path) {
print_message(msg_config, "chrooting into %s\n", chroot_path);
res = chroot(chroot_path);
CHECK_RES_DIE(res, "chroot");
}
if (user_name) {
set_keepcaps(1);
/* remove extraneous groups in case we belong to several extra groups
* that may have unwanted rights. If non-root when calling setgroups(),
* it fails, which is fine because... we have no unwanted rights
* (see POS36-C for security context)
* */
setgroups(0, NULL);
res = setgid(pw->pw_gid);
CHECK_RES_DIE(res, "setgid");
res = setuid(pw->pw_uid);
CHECK_RES_DIE(res, "setuid");
set_capabilities(use_transparent());
set_keepcaps(0);
}
}
/* Writes my PID */
void write_pid_file(const char* pidfile)
{
FILE *f;
int res;
f = fopen(pidfile, "w");
if (!f) {
print_message(msg_system_error, "write_pid_file:%s:%s", pidfile, strerror(errno));
exit(3);
}
res = fprintf(f, "%d\n", getpid());
if (res < 0) {
print_message(msg_system_error, "write_pid_file:fprintf:%s", strerror(errno));
exit(3);
}
res = fclose(f);
if (res == EOF) {
print_message(msg_system_error, "write_pid_file:fclose:%s", strerror(errno));
exit(3);
}
}
+186
View File
@@ -0,0 +1,186 @@
#ifndef COMMON_H
#define COMMON_H
/* FD_SETSIZE is 64 on Cygwin, which is really low. Just redefining it is
* enough for the macros to adapt (http://support.microsoft.com/kb/111855)
*/
#ifdef __CYGWIN__
#define FD_SETSIZE 4096
#endif
#define _GNU_SOURCE
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <pwd.h>
#include <syslog.h>
#include <libgen.h>
#include <time.h>
#include <getopt.h>
#ifdef LIBCAP
#include <sys/prctl.h>
#include <sys/capability.h>
#endif
#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__);
#else
#define TRACE
#endif
#ifndef IP_FREEBIND
#define IP_FREEBIND 0
#endif
#ifndef TCP_FASTOPEN
#define TCP_FASTOPEN 0
#endif
#ifndef TCP_FASTOPEN_CONNECT
#define TCP_FASTOPEN_CONNECT 30 /* Attempt FastOpen with connect. */
#endif
enum connection_state {
ST_PROBING=1, /* Waiting for timeout to find where to forward */
ST_SHOVELING /* Connexion is established */
};
/* 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 {
int fd;
void *begin_deferred_data;
void *deferred_data;
int deferred_data_size;
};
/* Double linked list for timeout management */
typedef struct {
struct connection* head;
struct connection* tail;
} dl_list;
struct connection {
int type; /* SOCK_DGRAM | SOCK_STREAM */
struct sslhcfg_protocols_item* proto; /* Where to connect to */
/* SOCK_STREAM */
enum connection_state state;
time_t probe_timeout;
/* q[0]: queue for external connection (client);
* q[1]: queue for internal connection (httpd or sshd);
* */
struct queue q[2];
/* SOCK_DGRAM */
struct sockaddr_storage client_addr; /* Contains the remote client address */
socklen_t addrlen;
int local_endpoint; /* Contains the local address */
time_t last_active;
/* double linked list of timeouts */
struct connection *timeout_prev, *timeout_next;
/* We need one local socket for each target server, so we know where to
* forward server responses */
int target_sock;
};
struct listen_endpoint {
int socketfd; /* file descriptor of listening socket */
int type; /* SOCK_DGRAM | SOCK_STREAM */
};
#define FD_CNXCLOSED 0
#define FD_NODATA -1
#define FD_STALLED -2
/* String description of a connection */
#define MAX_NAMELENGTH (NI_MAXHOST + NI_MAXSERV + 1)
struct connection_desc {
char peer[MAX_NAMELENGTH], service[MAX_NAMELENGTH],
local[MAX_NAMELENGTH], target[MAX_NAMELENGTH];
};
typedef enum {
NON_BLOCKING = 0,
BLOCKING = 1
} connect_blocking;
/* common.c */
void init_cnx(struct connection *cnx);
int set_nonblock(int fd);
int connect_addr(struct connection *cnx, int fd_from, connect_blocking blocking);
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);
void dump_connection(struct connection *cnx);
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 flush_deferred(struct queue *q);
extern struct sslhcfg_item cfg;
extern struct addrinfo *addr_listen;
extern const char* server_type;
/* sslh-fork.c */
void start_shoveler(int);
void main_loop(struct listen_endpoint *listen_sockets, int num_addr_listen);
#endif
+97
View File
@@ -0,0 +1,97 @@
#!/bin/sh
# SPDX-License-Identifier: GPL2-or-later
#
# Copyright (C) 2023 Olliver Schinagl <oliver@schinagl.nl>
#
# A beginning user should be able to docker run image bash (or sh) without
# needing to learn about --entrypoint
# https://github.com/docker-library/official-images#consistency
set -eu
bin='sslh'
# run command if it is not starting with a "-" and is an executable in PATH
if [ "${#}" -le 0 ] || \
[ "${1#-}" != "${1}" ] || \
[ -d "${1}" ] || \
! command -v "${1}" > '/dev/null' 2>&1; then
entrypoint='true'
fi
unconfigure_iptables() {
echo "Received SIG TERM/INT/KILL. Removing iptables / routing changes"
set +e # Don't exit if got error
set -x
iptables -t raw -D PREROUTING ! -i lo -d 127.0.0.0/8 -j DROP
iptables -t mangle -D POSTROUTING ! -o lo -s 127.0.0.0/8 -j DROP
iptables -t nat -D OUTPUT -m owner --uid-owner sslh -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -j CONNMARK --set-xmark 0x01/0x0f
iptables -t mangle -D OUTPUT ! -o lo -p tcp -m connmark --mark 0x01/0x0f -j CONNMARK --restore-mark --mask 0x0f
ip rule del fwmark 0x1 lookup 100
ip route del local 0.0.0.0/0 dev lo table 100
if [ $(cat /proc/sys/net/ipv6/conf/all/disable_ipv6) -eq 0 ]; then
ip6tables -t raw -D PREROUTING ! -i lo -d ::1/128 -j DROP
ip6tables -t mangle -D POSTROUTING ! -o lo -s ::1/128 -j DROP
ip6tables -t nat -D OUTPUT -m owner --uid-owner sslh -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -j CONNMARK --set-xmark 0x01/0x0f
ip6tables -t mangle -D OUTPUT ! -o lo -p tcp -m connmark --mark 0x01/0x0f -j CONNMARK --restore-mark --mask 0x0f
ip -6 rule del fwmark 0x1 lookup 100
ip -6 route del local ::/0 dev lo table 100
fi
set -e
set +x
}
configure_iptables() {
echo "Configuring iptables and routing..."
set +e # Don't exit if got error
set -x
iptables -t raw -A PREROUTING ! -i lo -d 127.0.0.0/8 -j DROP
iptables -t mangle -A POSTROUTING ! -o lo -s 127.0.0.0/8 -j DROP
iptables -t nat -A OUTPUT -m owner --uid-owner sslh -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -j CONNMARK --set-xmark 0x01/0x0f
iptables -t mangle -A OUTPUT ! -o lo -p tcp -m connmark --mark 0x01/0x0f -j CONNMARK --restore-mark --mask 0x0f
ip rule add fwmark 0x1 lookup 100
ip route add local 0.0.0.0/0 dev lo table 100
if [ $(cat /proc/sys/net/ipv6/conf/all/disable_ipv6) -eq 0 ]; then
ip6tables -t raw -A PREROUTING ! -i lo -d ::1/128 -j DROP
ip6tables -t mangle -A POSTROUTING ! -o lo -s ::1/128 -j DROP
ip6tables -t nat -A OUTPUT -m owner --uid-owner sslh -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -j CONNMARK --set-xmark 0x01/0x0f
ip6tables -t mangle -A OUTPUT ! -o lo -p tcp -m connmark --mark 0x01/0x0f -j CONNMARK --restore-mark --mask 0x0f
ip -6 rule add fwmark 0x1 lookup 100
ip -6 route add local ::/0 dev lo table 100
fi
set -e
set +x
}
for _args in "${@}" ; do
if [ "${_args:-}" = '--transparent' ] ; then
echo '--transparent flag is set'
configure_iptables
trap unconfigure_iptables TERM INT KILL
break
fi
done
# Drop privileges and run as sslh user
sslh_cmd="${entrypoint:+${bin}} ${@}"
echo "Executing with user 'sslh': ${sslh_cmd}"
exec su - sslh -c "${sslh_cmd}" &
wait "${!}"
exit 0
+169
View File
@@ -0,0 +1,169 @@
Frequently Asked Questions
==========================
When something doesn't work, look up here... and if it still
doesn't work, report how what was suggested here went.
It's also worth reading [how to ask
questions](http://www.catb.org/~esr/faqs/smart-questions.html)
before posting on the mailing list or opening an issue in
GitHub.
Getting more info
=================
In general, if something doesn't work, you'll want to run
`sslh` with lots of logging, and the logging directly in
the terminal (Otherwise, logs are sent to `syslog`, and
usually end up in `/var/log/auth.log`). You will achieve
this by running `sslh` in foreground with verbose:
```
sslh -v 1 -f -F myconfig.cfg
```
Higher values of `verbose` produce more information. 1 is
usually sufficient. 2 will also print incoming packets used
for probing.
forward to [PROBE] failed:connect: Connection refused
=====================================================
Usually this means `sslh` is configured to forward a
protocol somewhere, but no service is listening on the
target address. Check your `sslh` configuration, check the
corresponding server really is listening and running.
Finally, check the server is listening where you expect it
to:
```
netstat -lpt
```
I get a segmentation fault!
===========================
Well, it's not yours (fault): a segfault is always a bug in
the programme. Usually standard use cases are well tested,
so it may be related to something unusual in your
configuration, or even something wrong, but it should still
never result in a segfault.
Thankfully, when they are deterministic, segfaults are
usually fairly easy to fix if you're willing to run a few
diagnostics to help the developer.
First, make sure you have debug symbols:
```
$ file sslh-select
sslh-select: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=a758ac75ff11f1ace577705b4d6627e301940b59, with debug_info, not stripped
```
Note `with debug_info, not stripped` at the end. If you
don't have that, your distribution stripped the binary: you
will need to get the source code and compile it yourself
(that way, you will also get the latest version).
Install `valgrind` and run `sslh` under it:
```
valgrind --leak-check=full ./sslh-fork -v 2 -f -F yourconfig.cfg
```
Report the full output to the mailing list or github.
Valgrind is very powerful and gives precise hints of what is
wrong and why. For example on `sslh` issue
(#273)[https://github.com/yrutschle/sslh/issues/273]:
```
sudo valgrind --leak-check=full ./sslh-fork -v 2 -f -F /etc/sslh.cfg
==20037== Memcheck, a memory error detector
==20037== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==20037== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==20037== Command: ./sslh-fork -v 2 -f -F /etc/sslh.cfg
==20037==
sslh-fork v1.21b-1-g2c93a01-dirty started
--20037-- WARNING: unhandled arm-linux syscall: 403
--20037-- You may be able to write your own handler.
--20037-- Read the file README_MISSING_SYSCALL_OR_IOCTL.
--20037-- Nevertheless we consider this a bug. Please report
--20037-- it at http://valgrind.org/support/bug_reports.html.
==20040== Conditional jump or move depends on uninitialised value(s)
==20040== at 0x112A3C: parse_tls_header (tls.c:162)
==20040== by 0x111CEF: is_tls_protocol (probe.c:214)
==20040== by 0x11239F: probe_client_protocol (probe.c:366)
==20040== by 0x10A8F7: start_shoveler (sslh-fork.c:98)
==20040== by 0x10AE9B: main_loop (sslh-fork.c:200)
==20040== by 0x1114FB: main (sslh-main.c:322)
==20040==
```
Here we see that something wrong is happening at `tls.c`
line 162, and it's linked to an uninitialised value.
Using sslh for virtual hosting
==============================
Virtual hosting refers to having several domain names behind
a single IP address. All Web servers handle this, but
sometimes it can be useful to do it with `sslh`.
TLS virtual hosting with SNI
----------------------------
For TLS, this is done very simply using Server Name
Indication, SNI for short, which is a TLS extension whereby
the client indicates the name of the server it wishes to
connect to. This can be a very powerful way to separate
several TLS-based services hosted behind the same port:
simply name each service with its own hostname. For example,
we could define `mail.rutschle.net`, `im.rutschle.net`,
`www.rutschle.net`, all of which point to the same IP
address. `sslh` uses the `sni_hostnames` setting of the
TLS probe to do this, e.g.:
```
protocols: (
{ name: "tls";
host: "localhost";
port: "993";
sni_hostnames: [ "mail.rutschle.net" ];
},
{ name: "tls";
host: "localhost";
port: "xmpp-client";
sni_hostnames: [ "im.rutschle.net" ];
},
{ name: "tls";
host: "localhost";
port: "4443";
sni_hostnames: [ "www.rutschle.net" ];
}
);
```
HTTP virtual hosting with regex
-------------------------------
If you wish to serve several Web domains over HTTP through
`sslh`, you can do this simply by using regular expressions
on the Host specification part of the HTTP query.
The following example forwards connections to `host_A.acme`
to 192.168.0.2, and connections to `host_B.acme` to
192.168.0.3.
```
protocols: (
{ name: "regex";
host: "192.168.0.2";
port: "80";
regex_patterns:
["^(GET|POST|PUT|OPTIONS|DELETE|HEADER) [^ ]* HTTP/[0-9.]*[\r\n]*Host: host_A.acme"] },
{ name: "regex";
host: "192.168.0.3";
port: "80";
regex_patterns:
["^(GET|POST|PUT|OPTIONS|DELETE|HEADER) [^ ]* HTTP/[0-9.]*[\r\n]*Host: host_B.acme"] }
);
```
+144
View File
@@ -0,0 +1,144 @@
Compile and install
===================
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.
* [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.
* [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
* 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.
* 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`:
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)
(v1.5) to your path.
The test scripts are written in Perl, and will require
IO::Socket::INET6 (libio-socket-inet6-perl in Debian).
Compilation
-----------
After this, the Makefile should work:
make install
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).
* `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).
* `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.
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`:
* `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.
* `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.
Installation
------------
* In general:
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
* For CentOS:
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
+54
View File
@@ -0,0 +1,54 @@
sslh is available for Mac OS X via MacPorts. If you have
MacPorts installed on your system you can install sslh by
executing the following in the Terminal:
port install sslh
Also, the following is a helpful launchd configuration that
covers the most common use case of sslh. Save the following
into a text file, e.g.
/Library/LaunchDaemons/net.rutschle.sslh.plist, then load it
with launchctl or simply reboot.
----BEGIN FILE----
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Disabled</key>
<false/>
<key>KeepAlive</key>
<true/>
<key>Label</key>
<string>net.rutschle.sslh</string>
<key>ProgramArguments</key>
<array>
<string>/opt/local/sbin/sslh</string>
<string>-f</string>
<string>-v</string>
<string>-u</string>
<string>nobody</string>
<string>-p</string>
<string>0.0.0.0:443</string>
<string>--ssh</string>
<string>localhost:22</string>
<string>--tls</string>
<string>localhost:443</string>
</array>
<key>QueueDirectories</key>
<array/>
<key>RunAtLoad</key>
<true/>
<key>StandardErrorPath</key>
<string>/Library/Logs/sslh.log</string>
<key>StandardOutPath</key>
<string>/Library/Logs/sslh.log</string>
<key>WatchPaths</key>
<array/>
</dict>
</plist>
----END FILE----
+203
View File
@@ -0,0 +1,203 @@
Configuration
=============
If you use the scripts provided, sslh will get its
configuration from /etc/sslh.cfg. Please refer to
example.cfg for an overview of all the settings.
A good scheme is to use the external name of the machine in
`listen`, and bind `httpd` to `localhost:443` (instead of all
binding to all interfaces): that way, HTTPS connections
coming from inside your network don't need to go through
`sslh`, and `sslh` is only there as a frontal for connections
coming from the internet.
Note that 'external name' in this context refers to the
actual IP address of the machine as seen from your network,
i.e. that that is not `127.0.0.1` in the output of
`ifconfig(8)`.
Libwrap support
---------------
Sslh can optionally perform `libwrap` checks for the sshd
service: because the connection to `sshd` will be coming
locally from `sslh`, `sshd` cannot determine the IP of the
client.
OpenVPN support
---------------
OpenVPN clients connecting to OpenVPN running with
`-port-share` reportedly take more than one second between
the time the TCP connection is established and the time they
send the first data packet. This results in `sslh` with
default settings timing out and assuming an SSH connection.
To support OpenVPN connections reliably, it is necessary to
increase `sslh`'s timeout to 5 seconds.
Instead of using OpenVPN's port sharing, it is more reliable
to use `sslh`'s `--openvpn` option to get `sslh` to do the
port sharing.
Using proxytunnel with sslh
---------------------------
If you are connecting through a proxy that checks that the
outgoing connection really is SSL and rejects SSH, you can
encapsulate all your traffic in SSL using `proxytunnel` (this
should work with `corkscrew` as well). On the server side you
receive the traffic with `stunnel` to decapsulate SSL, then
pipe through `sslh` to switch HTTP on one side and SSL on the
other.
In that case, you end up with something like this:
ssh -> proxytunnel -e ----[ssh/ssl]---> stunnel ---[ssh]---> sslh --> sshd
Web browser -------------[http/ssl]---> stunnel ---[http]--> sslh --> httpd
Configuration goes like this on the server side, using `stunnel3`:
stunnel -f -p mycert.pem -d thelonious:443 -l /usr/local/sbin/sslh -- \
sslh -i --http localhost:80 --ssh localhost:22
* stunnel options:
* `-f` for foreground/debugging
* `-p` for specifying the key and certificate
* `-d` for specifying which interface and port
we're listening to for incoming connections
* `-l` summons `sslh` in inetd mode.
* sslh options:
* `-i` for inetd mode
* `--http` to forward HTTP connections to port 80,
and SSH connections to port 22.
Capabilities support
--------------------
On Linux (only?), you can compile sslh with `USELIBCAP=1` to
make use of POSIX capabilities; this will save the required
capabilities needed for transparent proxying for unprivileged
processes.
Alternatively, you may use filesystem capabilities instead
of starting sslh as root and asking it to drop privileges.
You will need `CAP_NET_BIND_SERVICE` for listening on port 443
and `CAP_NET_RAW` for transparent proxying (see
`capabilities(7)`).
You can use the `setcap(8)` utility to give these capabilities
to the executable:
sudo setcap cap_net_bind_service,cap_net_raw+pe sslh-select
Then you can run sslh-select as an unprivileged user, e.g.:
sslh-select -p myname:443 --ssh localhost:22 --tls localhost:443
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).
Systemd Socket Activation
-------------------------
If compiled with `USESYSTEMD` then it is possible to activate
the service on demand and avoid running any code as root.
In this mode any listen configuration options are ignored and
the sockets are passed by systemd to the service.
Example socket unit:
[Unit]
Before=sslh.service
[Socket]
ListenStream=1.2.3.4:443
ListenStream=5.6.7.8:444
ListenStream=9.10.11.12:445
FreeBind=true
[Install]
WantedBy=sockets.target
Example service unit:
[Unit]
PartOf=sslh.socket
[Service]
ExecStart=/usr/sbin/sslh -v -f --ssh 127.0.0.1:22 --tls 127.0.0.1:443
KillMode=process
CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_NET_RAW
PrivateTmp=true
PrivateDevices=true
ProtectSystem=full
ProtectHome=true
User=sslh
With this setup only the socket needs to be enabled. The sslh service
will be started on demand and does not need to run as root to bind the
sockets as systemd has already bound and passed them over. If the sslh
service is started on its own without the sockets being passed by systemd
then it will look to use those defined on the command line or config
file as usual. Any number of ListenStreams can be defined in the socket
file and systemd will pass them all over to sslh to use as usual.
To avoid inconsistency between starting via socket and starting directly
via the service Requires=sslh.socket can be added to the service unit to
mandate the use of the socket configuration.
Rather than overwriting the entire socket file drop in values can be placed
in /etc/systemd/system/sslh.socket.d/<name>.conf with additional ListenStream
values that will be merged.
In addition to the above with manual .socket file configuration there is an
optional systemd generator which can be compiled - systemd-sslh-generator
This parses the /etc/sslh.cfg (or /etc/sslh/sslh.cfg file if that exists
instead) configuration file and dynamically generates a socket file to use.
This will also merge with any sslh.socket.d drop in configuration but will be
overridden by a /etc/systemd/system/sslh.socket file.
To use the generator place it in /usr/lib/systemd/system-generators and then
call systemctl daemon-reload after any changes to /etc/sslh.cfg to generate
the new dynamic socket unit.
Fail2ban
--------
If using transparent proxying, just use the standard ssh
rules. If you can't or don't want to use transparent
proxying, you can set `fail2ban` rules to block repeated ssh
connections from an IP address (obviously this depends
on the site, there might be legitimate reasons you would get
many connections to ssh from the same IP address...)
See example files in scripts/fail2ban.
UDP
---
`sslh` can perform demultiplexing on UDP packets as well.
This does not work with `sslh-fork` (it is not possible to
support UDP with a forking model). Specify a listening
address and target protocols with `is_udp: true`. `sslh`
will wait for incoming UDP packets, run the probes in the
usual fashion, and forward packets to the appropriate
target. `sslh` will then remember the association between
remote host to target server for 60 seconds by default,
which can be overridden with `udp_timeout`. This allows to
process both single-datagram protocols such as DNS, and
connection-based protocols such as QUIC.
An example for supporting QUIC is shown in `example.cfg`.
+430
View File
@@ -0,0 +1,430 @@
# Transparent proxy
On Linux and FreeBSD you can use the `--transparent` option to
request transparent proxying. This means services behind `sslh`
(Apache, `sshd` and so on) will see the external IP and ports
as if the external world connected directly to them. This
simplifies IP-based access control (or makes it possible at
all).
This document shows recipes that may help to do that.
Note that getting this to work is very tricky and
detail-dependant: depending on whether the target server and
sslh are on the same machine, different machines, or
different dockers, and tool versions, all seem to change the
required network configuration somewhat. If it doesn't work,
it's almost certain that the problem is not linked to `sslh`
but to the network setup that surrounds it. If in trouble,
it might be worth trying to set up the network rules
with a simpler server than `sslh`, such as
[`socat`](http://www.dest-unreach.org/socat/)
Users have tried to do at least the following:
* `sslh` and the target servers run on the same host (see [below](#transparent-proxy-to-one-host))
* `sslh` runs on a host, and the target servers run on LXC or dockers running on the same host. No known working setup.
* `sslh` runs on a host, and the target servers run on different hosts on the same local network(see [below](#transparent-proxy-to-two-hosts))
* `sslh` runs on a host, and the target servers run on a different host on a different network (there is a [usecase](https://github.com/yrutschle/sslh/issues/295) for this). No known working setup, and it's unclear it is possible.
## Transparent proxy to one host
### Linux
`sslh` needs extended rights to perform this: you'll need to
give it `CAP_NET_RAW` capabilities (see appropriate chapter)
or run it as root (but don't do that).
The firewalling tables also need to be adjusted as follows.
I don't think it is possible to have `httpd` and `sslh` both listen to 443 in
this scheme -- let me know if you manage that:
# Set route_localnet = 1 on all interfaces so that ssl can use "localhost" as destination
sysctl -w net.ipv4.conf.default.route_localnet=1
sysctl -w net.ipv4.conf.all.route_localnet=1
# DROP martian packets as they would have been if route_localnet was zero
# Note: packets not leaving the server aren't affected by this, thus sslh will still work
iptables -t raw -A PREROUTING ! -i lo -d 127.0.0.0/8 -j DROP
iptables -t mangle -A POSTROUTING ! -o lo -s 127.0.0.0/8 -j DROP
# Mark all connections made by ssl for special treatment (here sslh is run as user "sslh")
iptables -t nat -A OUTPUT -m owner --uid-owner sslh -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -j CONNMARK --set-xmark 0x01/0x0f
# Outgoing packets that should go to sslh instead have to be rerouted, so mark them accordingly (copying over the connection mark)
iptables -t mangle -A OUTPUT ! -o lo -p tcp -m connmark --mark 0x01/0x0f -j CONNMARK --restore-mark --mask 0x0f
# Configure routing for those marked packets
ip rule add fwmark 0x1 lookup 100
ip route add local 0.0.0.0/0 dev lo table 100
Transparent proxying with IPv6 is similarly set up as follows:
# Set route_localnet = 1 on all interfaces so that ssl can use "localhost" as destination
# Not sure if this is needed for ipv6 though
sysctl -w net.ipv4.conf.default.route_localnet=1
sysctl -w net.ipv4.conf.all.route_localnet=1
# DROP martian packets as they would have been if route_localnet was zero
# Note: packets not leaving the server aren't affected by this, thus sslh will still work
ip6tables -t raw -A PREROUTING ! -i lo -d ::1/128 -j DROP
ip6tables -t mangle -A POSTROUTING ! -o lo -s ::1/128 -j DROP
# Mark all connections made by ssl for special treatment (here sslh is run as user "sslh")
ip6tables -t nat -A OUTPUT -m owner --uid-owner sslh -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -j CONNMARK --set-xmark 0x01/0x0f
# Outgoing packets that should go to sslh instead have to be rerouted, so mark them accordingly (copying over the connection mark)
ip6tables -t mangle -A OUTPUT ! -o lo -p tcp -m connmark --mark 0x01/0x0f -j CONNMARK --restore-mark --mask 0x0f
# Configure routing for those marked packets
ip -6 rule add fwmark 0x1 lookup 100
ip -6 route add local ::/0 dev lo table 100
Explanation:
To be able to use `localhost` as destination in your sslh config along with transparent proxying
you have to allow routing of loopback addresses as done above.
This is something you usually should not do (see [this stackoverflow post](https://serverfault.com/questions/656279/how-to-force-linux-to-accept-packet-with-loopback-ip/656484#656484))
The two `DROP` iptables rules emulate the behaviour of `route_localnet` set to off (with one small difference:
allowing the reroute-check to happen after the fwmark is set on packets destined for sslh).
See [this diagram](https://upload.wikimedia.org/wikipedia/commons/3/37/Netfilter-packet-flow.svg) for a good visualisation
showing how packets will traverse the iptables chains.
Note:
You have to run `sslh` as dedicated user (in this example the user is also named `sslh`), to not mess up with your normal networking.
These rules will allow you to connect directly to ssh on port
22 (or to any other service behind sslh) as well as through sslh on port 443.
Also remember that iptables configuration and ip routes and
rules won't be necessarily persisted after you reboot. Make
sure to save them properly. For example in CentOS7, you would
do `iptables-save > /etc/sysconfig/iptables`, and add both
`ip` commands to your `/etc/rc.local`.
### FreeBSD
Given you have no firewall defined yet, you can use the following configuration
to have ipfw properly redirect traffic back to sslh
/etc/rc.conf
firewall_enable="YES"
firewall_type="open"
firewall_logif="YES"
firewall_coscripts="/etc/ipfw/sslh.rules"
/etc/ipfw/sslh.rules
#! /bin/sh
# ssl
ipfw add 20000 fwd 192.0.2.1,443 log tcp from 192.0.2.1 8443 to any out
ipfw add 20010 fwd 2001:db8::1,443 log tcp from 2001:db8::1 8443 to any out
# ssh
ipfw add 20100 fwd 192.0.2.1,443 log tcp from 192.0.2.1 8022 to any out
ipfw add 20110 fwd 2001:db8::1,443 log tcp from 2001:db8::1 8022 to any out
# xmpp
ipfw add 20200 fwd 192.0.2.1,443 log tcp from 192.0.2.1 5222 to any out
ipfw add 20210 fwd 2001:db8::1,443 log tcp from 2001:db8::1 5222 to any out
# openvpn (running on other internal system)
ipfw add 20300 fwd 192.0.2.1,443 log tcp from 198.51.100.7 1194 to any out
ipfw add 20310 fwd 2001:db8::1,443 log tcp from 2001:db8:1::7 1194 to any out
General notes:
This will only work if `sslh` does not use any loopback
addresses (no `127.0.0.1` or `localhost`), you'll need to use
explicit IP addresses (or names):
sslh --listen 192.168.0.1:443 --ssh 192.168.0.1:22 --tls 192.168.0.1:4443
This will not work:
sslh --listen 192.168.0.1:443 --ssh 127.0.0.1:22 --tls 127.0.0.1:4443
Transparent proxying means the target server sees the real
origin address, so it means if the client connects using
IPv6, the server must also support IPv6. It is easy to
support both IPv4 and IPv6 by configuring the server
accordingly, and setting `sslh` to connect to a name that
resolves to both IPv4 and IPv6, e.g.:
sslh --transparent --listen <extaddr>:443 --ssh insideaddr:22
/etc/hosts:
192.168.0.1 insideaddr
201::::2 insideaddr
Upon incoming IPv6 connection, `sslh` will first try to
connect to the IPv4 address (which will fail), then connect
to the IPv6 address.
## Transparent Proxy to Two Hosts
Tutorial by Sean Warner. 19 June 2019 20:35
### Aim
* Show that `sslh` can transparently proxy requests from the internet to services on two separate hosts that are both on the same LAN.
* The IP address of the client initiating the request is what the destination should see… and not the IP address of the host that `sslh` is running on, which is what happens when `sslh` is not running in transparent mode.
* The solution here only works for my very specific use-case but hopefully others can adapt it to suits their needs.
### Overview of my Network
Two Raspberry Pis on my home LAN:
* Pi A: 192.168.1.124 – `sslh` (Port 4433), Apache2 web server for https (port 443), `stunnel` (port 4480) to decrypt ssh traffic and forward to SSH server (also on Pi A at Port 1022)
* Pi B: 192.168.1.123 - HTTP server (port 8000), SSH server (port 1022 on PiB).
* I send traffic from the internet to my router's external port 443 then use a port forward rule in my router to map that to internal port 4433 where sslh is listening.
![Architecture](tproxy.svg)
### `sslh` build
 
`sslh` Version: sslh v1.19c-2-gf451cc8-dirty.
I compiled sslh from sources giving the binary pretty much all possible options such as Posix capabilities and systemd support.. here are the first few lines of the makefile:
 
```
# Configuration
VERSION=$(shell ./genver.sh -r)
ENABLE_REGEX=1         # Enable regex probes
USELIBCONFIG=1         # Use libconfig? (necessary to use configuration files)
USELIBPCRE=1 # Use libpcre? (needed for regex on musl)
USELIBWRAP=1         # Use libwrap?
USELIBCAP=1         # Use libcap?
USESYSTEMD=1         # Make use of systemd socket activation
COV_TEST=         # Perform test coverage?
PREFIX=/usr/local
BINDIR=$(PREFIX)/sbin
MANDIR=$(PREFIX)/share/man/man8
MAN=sslh.8.gz         # man page name
# End of configuration -- the rest should take care of
# itself
```
 
### systemd setup
Create an sslh systemd service file...
```
# nano /lib/systemd/system/sslh.service
```
Paste in this contents…
```
[Unit]
Description=SSL/SSH multiplexer
After=network.target
Documentation=man:sslh(8)
[Service]
#EnvironmentFile=/etc/default/sslh
#ExecStart=/usr/local/sbin/sslh $DAEMON_OPTS
ExecStart=/usr/local/sbin/sslh -F /etc/sslh/sslh.cfg
KillMode=process
[Install]
WantedBy=multi-user.target
```
Save it and then…
```
# systemctl daemon-reload
```
Start it again to test…
```
# systemctl start sslh
```
 
### Configure `sslh`
First stop `sslh` then open the config file and replace with below, save and start `sslh` again
```
# systemctl stop sslh
# nano /etc/sslh/sslh.cfg
# systemctl start sslh
```
```
verbose: true;
foreground: true;
inetd: false;
numeric: true;
transparent: true;
timeout: 2;
user: "sslh";
pidfile: "/var/run/sslh.pid";
chroot: "/var/empty";
# You must have a port forward rule in the router: external port 443 <-> internal port 4433
# Local ip address of PiA is: 192.168.1.124, sslh and stunnel4 are running on this Pi
# Local ip address of PiB is: 192.168.1.123, http server and ssh server on this Pi
listen:
(
{ host: "192.168.1.124"; port: "4433"; }
);
# sslh demultiplexes based on the Protocol and Hostname
protocols:
(
{ name: "tls"; sni_hostnames: [ "www.example.com" ]; host: "192.168.1.124"; port: "443"; log_level: 1; },
# This probe is for tls encrypted ssh. SSLH forwards it to stunnel on port 4480 which decrypts it and sends it to the ssh server on PiA port 1022
{ name: "tls"; sni_hostnames: [ "ssh.example.com" ]; host: "192.168.1.124"; port: "4480"; log_level: 1; },
{ name: "http"; host: "192.168.1.123"; port: "8000"; log_level: 1; },
{ name: "ssh"; host: "192.168.1.123"; port: "1022"; log_level: 1; }
);
```
 
### Configure `stunnel`
First stop `stunnel` then open the config file and replace with below, save and start `stunnel` again
```
# systemctl stop stunnel4
# nano /etc/stunnel/stunnel.conf
# systemctl start stunnel4
```
```
# Debugging stuff (may be useful for troubleshooting)
foreground = yes
#debug = 5 # this is the default
debug = 7
output = /var/log/stunnel4/stunnel.log
pid = /var/run/stunnel4/stunnel.pid
fips = no
cert = /etc/letsencrypt/live/example.com/fullchain.pem
key = /etc/letsencrypt/live/example.com/privkey.pem
[ssh]
accept = 192.168.1.124:4480
connect = 192.168.1.124:1022
TIMEOUTclose = 0
```
 
### Configure iptables for Pi A
The `_add.sh` script creates the rules, the `_rm.sh` script removes the rules.
They will be lost if you reboot but there are ways to make them load again on start-up..
```
# nano /usr/local/sbin/piA_tproxy_add.sh
```
``` piA_tproxy_add.sh
iptables -t mangle -N SSLH
iptables -t mangle -A PREROUTING -p tcp -m socket --transparent -j SSLH
iptables -t mangle -A OUTPUT --protocol tcp --out-interface eth0 -m multiport --sport 443,4480 --jump SSLH
iptables -t mangle -A SSLH --jump MARK --set-mark 0x1
iptables -t mangle -A SSLH --jump ACCEPT
ip rule add fwmark 0x1 lookup 100
ip route add local 0.0.0.0/0 dev lo table 100
```
```
# nano /usr/local/sbin/piA_tproxy_rm.sh
```
``` piA_tproxy_rm.sh
iptables -t mangle -D PREROUTING -p tcp -m socket --transparent -j SSLH
iptables -t mangle -D OUTPUT --protocol tcp --out-interface eth0 -m multiport --sport 443,4480 --jump SSLH
iptables -t mangle -D SSLH --jump MARK --set-mark 0x1
iptables -t mangle -D SSLH --jump ACCEPT
iptables -t mangle -X SSLH
ip rule del fwmark 0x1 lookup 100
ip route del local 0.0.0.0/0 dev lo table 100
```
Make them executable..
```
# chmod +rx piA_tproxy_add.sh
# chmod +rx piA_tproxy_rm.sh
```
Now run the "add" script on Pi A!
```
# piA_tproxy_add.sh
# piA_tproxy_rm.sh
```
## Configure iptables for Pi B
```
# nano /usr/local/sbin/piB_tproxy_add.sh
```
``` piB_tproxy_add.sh
iptables -t mangle -N SSLHSSL
iptables -t mangle -A OUTPUT -o eth0 -p tcp -m multiport --sport 1022,8000 -j SSLHSSL
iptables -t mangle -A SSLHSSL --jump MARK --set-mark 0x1
iptables -t mangle -A SSLHSSL --jump ACCEPT
ip rule add fwmark 0x1 lookup 100
ip route add default via 192.168.1.124 table 100
ip route flush cache
```
```
# nano /usr/local/sbin/piB_tproxy_rm.sh
```
```
iptables -t mangle -D OUTPUT -o eth0 -p tcp -m multiport --sport 1022,8000 -j SSLHSSL
iptables -t mangle -D SSLHSSL --jump MARK --set-mark 0x1
iptables -t mangle -D SSLHSSL --jump ACCEPT
iptables -t mangle -X SSLHSSL
ip rule del fwmark 0x1 lookup 100
ip route del default via 192.168.1.124 table 100
ip route flush cache
```
Make them executable..
```
# chmod +rx piB_tproxy_add.sh
# chmod +rx piB_tproxy_rm.sh
```
Now run the "add" script on Pi B!
```
# piB_tproxy_add.sh
# piB_tproxy_rm.sh
```
 
### Testing
* Getting to sshd on PiA
I did this test using 4G from my phone (outside the LAN)
To simulate this I use `proxytunnel`. External port 443 is forwarded by my router to 4433. I need to arrive at `sslh` (port 4433) with ssh encrypted as TLS (hence I use the -e switch) and the `sni_hostname` set to ssh.example.com so that `sslh` will demultiplex to `stunnel` (port 4480) which will decrypt and forward to ssh server on PiA… see `sslh.cfg` and `stunnel.conf`.
The first IP:port is just a free HTTPS proxy I found on https://free-proxy-list.net
I execute this command from a terminal window..
```
# proxytunnel -v -e -C root.pem -p 78.141.192.198:8080 -d ssh.example.com:443
```
* Getting to sshd on PiB
I did this test using 4G from my phone (outside the LAN)
My smartphone telecom provider blocks ssh over port 443 so I need to use `proxytunnel` to encrypt.
Use the Proxytunnel `-X` switch to encrypt from local proxy to destination only so by the time we get to the destination it is unencrypted and `sslh` will see the ssh protocol and demultiplex to PiB as per `sslh.cfg`.
```
# proxytunnel -v -X -C root.pem -p 78.141.192.198:8080 -d ssh.example.com:443
```
Now when you test it all look at the output in daemon.log like this:
```
# grep -i 'ssl' /var/log/daemon.log
```
You should see that the IP address and port from the “connection from” and “forwarded from” fields are the same.
Special thanks and appreciation to Michael Yelsukov without whom I would never have got this working.
Any feedback or corrections very welcome!
+635
View File
@@ -0,0 +1,635 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="297mm"
height="210mm"
viewBox="0 0 297 210"
version="1.1"
id="svg288"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="Transparent Proxy to Two Hosts-new.svg">
<defs
id="defs282">
<linearGradient
id="linearGradient4840"
osb:paint="solid">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop4838" />
</linearGradient>
<linearGradient
id="linearGradient4802"
osb:paint="solid">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop4800" />
</linearGradient>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.01"
inkscape:cx="511.12411"
inkscape:cy="388.13656"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:snap-page="true"
inkscape:window-width="1920"
inkscape:window-height="1005"
inkscape:window-x="-9"
inkscape:window-y="-9"
inkscape:window-maximized="1"
showguides="false" />
<metadata
id="metadata285">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-87)">
<rect
style="fill:#000000;fill-opacity:0;stroke:#fa9600;stroke-width:0.68479234;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4977-0"
width="37.333149"
height="17.888765"
x="234.24397"
y="232.81883" />
<ellipse
id="path290"
cx="38.405174"
cy="133.55173"
rx="21.853449"
ry="28.965515"
style="stroke-width:0.26458332;fill-opacity:1;fill:none" />
<ellipse
style="fill:none;stroke-width:0.26458332"
id="path4806"
cx="44.482758"
cy="135.49138"
rx="28.965515"
ry="27.025862" />
<ellipse
style="fill:none;stroke-width:0.26458332"
id="path4808"
cx="124.26724"
cy="151.26724"
rx="29.612068"
ry="24.698277" />
<ellipse
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.76499999;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path4812"
ry="30.796005"
rx="24.16864"
cy="140.54999"
cx="43.31974" />
<rect
style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:0.59869254;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect4852"
width="27.302826"
height="13.554869"
x="29.797318"
y="133.28833" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:6.6431241px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.31139645"
x="38.145908"
y="130.0871"
id="text138"
transform="scale(0.9172844,1.0901744)"><tspan
sodipodi:role="line"
x="38.145908"
y="130.0871"
id="tspan136"
style="stroke-width:0.31139645"><tspan
x="38.145908"
y="130.0871"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93888903px;font-family:'Courier New';-inkscape-font-specification:'Courier New';stroke-width:0.31139645"
id="tspan134">Client</tspan></tspan></text>
<rect
style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:0.66499996;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect4862"
width="20.232847"
height="37.612331"
x="101.42361"
y="121.64869" />
<rect
style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:0.67072004;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect4864"
width="121.91585"
height="91.301468"
x="156.41544"
y="116.72592" />
<rect
style="fill:#000000;fill-opacity:0;stroke:#000000;stroke-width:0.66499996;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect4866"
width="122.95342"
height="37.871742"
x="155.89668"
y="223.04117" />
<rect
style="fill:#000000;fill-opacity:0;stroke:#f40000;stroke-width:0.701;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect4868"
width="82.747147"
height="23.645355"
x="175.0919"
y="128.09322" />
<rect
style="fill:#000000;fill-opacity:0;stroke:#009600;stroke-width:0.66499996;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect4870-7"
width="26.977121"
height="14.785542"
x="220.4861"
y="185.84906" />
<rect
style="fill:#000000;fill-opacity:0;stroke:#009600;stroke-width:0.66499996;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect4870-7-1"
width="26.977123"
height="14.785542"
x="184.17078"
y="185.84906" />
<rect
style="fill:#000000;fill-opacity:0;stroke:#009600;stroke-width:0.66499996;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect4919"
width="27.495911"
height="11.413391"
x="219.96732"
y="164.44894" />
<rect
style="fill:none;fill-opacity:0;stroke:#800080;stroke-width:0.85740757;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.96517412"
id="rect4921"
width="139.8217"
height="161.06387"
x="148.11099"
y="108.15634" />
<path
style="fill:#000000;stroke:#c98f26;stroke-width:0.665;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:0.88627452;stroke-miterlimit:4;stroke-dasharray:none;fill-opacity:0"
d="m 57.326389,140.58456 c 43.837821,-0.25939 44.097221,-0.25939 44.097221,-0.25939"
id="path4925"
inkscape:connector-curvature="0" />
<path
style="fill:#000000;fill-opacity:0;stroke:#c98f26;stroke-width:0.665;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.88627451"
d="m 122.01937,140.18462 c 53.1252,-0.25845 53.43955,-0.25845 53.43955,-0.25845"
id="path4925-7"
inkscape:connector-curvature="0" />
<path
style="fill:#000000;fill-opacity:0;stroke:#c98f26;stroke-width:0.58399999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.75200005, 0.58400002;stroke-dashoffset:0;stroke-opacity:0.88627451"
d="m 101.35344,140.71552 c 19.77892,-0.26191 19.89596,-0.26191 19.89596,-0.26191"
id="path4925-4"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#009600;stroke-width:0.665;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="m 198.1781,185.71937 c -0.25939,-33.9808 -0.25939,-33.9808 -0.25939,-33.9808"
id="path4971"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#009600;stroke-width:0.665;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="m 233.45588,164.18955 c 0,-12.45098 0,-12.45098 0,-12.45098"
id="path4973"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#009600;stroke-width:0.665;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="m 233.19648,185.45997 c 0,-9.59763 0,-9.59763 0,-9.59763"
id="path4975"
inkscape:connector-curvature="0" />
<rect
style="fill:none;fill-opacity:0;stroke:#fa9600;stroke-width:0.665;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4977"
width="37.35294"
height="16.860703"
x="161.2299"
y="233.1991" />
<path
style="fill:none;stroke:#fa9600;stroke-width:0.665;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:0.60784316;stroke-miterlimit:4;stroke-dasharray:none"
d="m 178.79609,151.73857 c 0.25941,81.45016 0.25941,81.45016 0.25941,81.45016"
id="path4994"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#fa9600;stroke-width:0.66078299;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.60784316"
d="m 253.45564,151.97084 c 0.25959,80.36694 0.25959,80.36694 0.25959,80.36694"
id="path4994-0"
inkscape:connector-curvature="0" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="32.424427"
y="103.23161"
id="text5013"><tspan
sodipodi:role="line"
id="tspan5011"
x="32.424427"
y="103.23161"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:5.29166698px;line-height:0;font-family:'Courier New';-inkscape-font-specification:'Courier New';stroke-width:0.26458332">Internet</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="102.3172"
y="116.44047"
id="text5013-6"><tspan
sodipodi:role="line"
id="tspan5011-2"
x="102.3172"
y="116.44047"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:5.29166698px;line-height:0;font-family:'Courier New';-inkscape-font-specification:'Courier New';stroke-width:0.26458332">Router</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#800080;fill-opacity:0.96470588;stroke:none;stroke-width:0.26458332"
x="188.76727"
y="105.30679"
id="text5053"><tspan
sodipodi:role="line"
id="tspan5051"
x="188.76727"
y="105.30679"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.93888903px;font-family:'Courier New';-inkscape-font-specification:'Courier New';fill:#800080;fill-opacity:0.96470588;stroke:none;stroke-width:0.26458332;stroke-opacity:0.96517412">Local Area Network</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.58333302px;line-height:1.25;font-family:'Courier New';-inkscape-font-specification:'Courier New';letter-spacing:0px;word-spacing:0px;fill:#c98f26;fill-opacity:0.88627451;stroke:none;stroke-width:0.26458332"
x="92.344772"
y="138.5094"
id="text5057"><tspan
sodipodi:role="line"
id="tspan5055"
x="92.344772"
y="138.5094"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.23333311px;font-family:'Courier New';-inkscape-font-specification:'Courier New';fill:#c98f26;fill-opacity:0.88627451;stroke-width:0.26458332">443</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.58333302px;line-height:1.25;font-family:'Courier New';-inkscape-font-specification:'Courier New';letter-spacing:0px;word-spacing:0px;fill:#c98f26;fill-opacity:0.88627451;stroke:none;stroke-width:0.26458332"
x="92.414749"
y="138.5529"
id="text5057-0"><tspan
sodipodi:role="line"
id="tspan5055-6"
x="92.414749"
y="138.5529"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.23333311px;font-family:'Courier New';-inkscape-font-specification:'Courier New';fill:#c98f26;fill-opacity:0.88627451;stroke-width:0.26458332">443</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.58333302px;line-height:1.25;font-family:'Courier New';-inkscape-font-specification:'Courier New';letter-spacing:0px;word-spacing:0px;fill:#c98f26;fill-opacity:0.88627451;stroke:none;stroke-width:0.26458332"
x="92.414749"
y="138.5529"
id="text5057-0-8"><tspan
sodipodi:role="line"
id="tspan5055-6-2"
x="92.414749"
y="138.5529"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.23333333px;font-family:'Courier New';-inkscape-font-specification:'Courier New';fill:#c98f26;fill-opacity:0.88627451;stroke-width:0.26458332">443</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.3396225px;line-height:1.25;font-family:'Courier New';-inkscape-font-specification:'Courier New';letter-spacing:0px;word-spacing:0px;fill:#c98f26;fill-opacity:0.88627451;stroke:none;stroke-width:0.28349054"
x="151.81532"
y="147.89809"
id="text5057-0-8-6"
transform="scale(1.0714604,0.93330561)"><tspan
sodipodi:role="line"
id="tspan5055-6-2-7"
x="151.81532"
y="147.89809"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.23333333px;font-family:'Courier New';-inkscape-font-specification:'Courier New';fill:#c98f26;fill-opacity:0.88627451;stroke-width:0.28349054">4433</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:11.33962345px;line-height:1.25;font-family:'Courier New';-inkscape-font-specification:'Courier New';letter-spacing:0px;word-spacing:0px;fill:#c98f26;fill-opacity:0.88627451;stroke:none;stroke-width:0.28349057"
x="114.9891"
y="148.08023"
id="text5057-0-8-6-4"
transform="scale(1.0714604,0.93330561)"><tspan
sodipodi:role="line"
id="tspan5055-6-2-7-3"
x="114.9891"
y="148.08023"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.23333311px;font-family:'Courier New';-inkscape-font-specification:'Courier New';fill:#c98f26;fill-opacity:0.88627451;stroke-width:0.28349057">4433</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="208.55414"
y="135.19525"
id="text144"><tspan
sodipodi:role="line"
x="208.55414"
y="135.19525"
id="tspan142"
style="stroke-width:0.26458332"><tspan
x="208.55414"
y="135.19525"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:5.64444447px;font-family:'Courier New';-inkscape-font-specification:'Courier New';fill:#f40000;fill-opacity:1;stroke-width:0.26458332"
id="tspan140">SSLH</tspan></tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:3.52777767px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="158.75"
y="121.48432"
id="text5159"><tspan
sodipodi:role="line"
id="tspan5157"
x="158.75"
y="121.48432"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';stroke-width:0.26458332;font-size:3.88055556px">192.168.1.124 (Pi A)</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:3.52777767px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="157.30107"
y="258.37653"
id="text5159-0"><tspan
sodipodi:role="line"
id="tspan5157-1"
x="157.30107"
y="258.37653"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88055563px;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';stroke-width:0.26458332">192.168.1.123 (Pi B)</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:3.52777767px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#fa9600;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="167.25511"
y="242.15076"
id="text168"><tspan
sodipodi:role="line"
x="167.25511"
y="242.15076"
id="tspan166"
style="stroke-width:0.26458332"><tspan
x="167.25511"
y="242.15076"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.23333311px;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';fill:#fa9600;fill-opacity:1;stroke-width:0.26458332"
id="tspan164">Web Server</tspan></tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:3.52777767px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#fa9600;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="239.92085"
y="241.98419"
id="text174"><tspan
sodipodi:role="line"
x="239.92085"
y="241.98419"
id="tspan172"
style="stroke-width:0.26458332"><tspan
x="239.92085"
y="241.98419"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:4.23333311px;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';fill:#fa9600;fill-opacity:1;stroke-width:0.26458332"
id="tspan170">SSH Server</tspan></tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:3.52777767px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="167.9187"
y="190.6398"
id="text186"><tspan
sodipodi:role="line"
x="167.9187"
y="190.6398"
id="tspan184"
style="stroke-width:0.26458332"><tspan
x="167.9187"
y="190.6398"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88055563px;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';fill:#fa9600;fill-opacity:1;stroke-width:0.26458332"
id="tspan182">http</tspan></tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:3.52777767px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="167.48399"
y="157.93468"
id="text180"><tspan
sodipodi:role="line"
x="167.48399"
y="157.93468"
id="tspan178"
style="stroke-width:0.26458332"><tspan
x="167.48399"
y="157.93468"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88055563px;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';fill:#fa9600;fill-opacity:1;stroke-width:0.26458332"
id="tspan176">8000</tspan></tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:3.52777767px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="167.74596"
y="229.97469"
id="text192"><tspan
sodipodi:role="line"
x="167.74596"
y="229.97469"
id="tspan190"
style="stroke-width:0.26458332"><tspan
x="167.74596"
y="229.97469"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88055563px;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';fill:#fa9600;fill-opacity:1;stroke-width:0.26458332"
id="tspan188">8000</tspan></tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:3.52777767px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="255.3237"
y="190.91431"
id="text246"><tspan
sodipodi:role="line"
x="255.3237"
y="190.91431"
id="tspan244"
style="stroke-width:0.26458332"><tspan
x="255.3237"
y="190.91431"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88055563px;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';fill:#fa9600;fill-opacity:1;stroke-width:0.26458332"
id="tspan242">ssh</tspan></tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:3.52777767px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="254.88899"
y="158.20918"
id="text240"><tspan
sodipodi:role="line"
x="254.88899"
y="158.20918"
id="tspan238"
style="stroke-width:0.26458332"><tspan
x="254.88899"
y="158.20918"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88055563px;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';fill:#fa9600;fill-opacity:1;stroke-width:0.26458332"
id="tspan236">1022</tspan></tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:3.52777767px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="255.15096"
y="230.24921"
id="text252"><tspan
sodipodi:role="line"
x="255.15096"
y="230.24921"
id="tspan250"
style="stroke-width:0.26458332"><tspan
x="255.15096"
y="230.24921"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88055563px;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';fill:#fa9600;fill-opacity:1;stroke-width:0.26458332"
id="tspan248">1022</tspan></tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:3.52777767px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#009600;fill-opacity:1;stroke:none;stroke-width:0.26458332;stroke-opacity:1"
x="186.26637"
y="193.70544"
id="text156"><tspan
sodipodi:role="line"
x="186.26637"
y="193.70544"
id="tspan154"
style="stroke-width:0.26458332"><tspan
x="186.26637"
y="193.70544"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88055563px;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';fill:#009600;fill-opacity:1;stroke:none;stroke-width:0.26458332;stroke-opacity:1"
id="tspan152">Web Server</tspan></tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:3.52777767px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#009600;fill-opacity:1;stroke:none;stroke-width:0.26458332;stroke-opacity:1"
x="225.25404"
y="170.88316"
id="text150"><tspan
sodipodi:role="line"
x="225.25404"
y="170.88316"
id="tspan148"
style="stroke-width:0.26458332"><tspan
x="225.25404"
y="170.88316"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88055563px;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';fill:#009600;fill-opacity:1;stroke:none;stroke-width:0.26458332;stroke-opacity:1"
id="tspan146">STUNNEL</tspan></tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:3.52777767px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#009600;fill-opacity:1;stroke:none;stroke-width:0.26458332;stroke-opacity:1"
x="222.6344"
y="194.19792"
id="text162"><tspan
sodipodi:role="line"
x="222.6344"
y="194.19792"
id="tspan160"
style="stroke-width:0.26458332"><tspan
x="222.6344"
y="194.19792"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88055563px;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';fill:#009600;fill-opacity:1;stroke:none;stroke-width:0.26458332;stroke-opacity:1"
id="tspan158">SSH Server</tspan></tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:3.52777767px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#009600;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="199.15128"
y="157.29231"
id="text198"><tspan
sodipodi:role="line"
x="199.15128"
y="157.29231"
id="tspan196"
style="stroke-width:0.26458332"><tspan
x="199.15128"
y="157.29231"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88055563px;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';fill:#009600;fill-opacity:1;stroke-width:0.26458332"
id="tspan194">443</tspan></tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:3.52777767px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#009600;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="199.41324"
y="182.17886"
id="text210"><tspan
sodipodi:role="line"
x="199.41324"
y="182.17886"
id="tspan208"
style="stroke-width:0.26458332"><tspan
x="199.41324"
y="182.17886"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88055563px;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';fill:#009600;fill-opacity:1;stroke-width:0.26458332"
id="tspan206">443</tspan></tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:3.52777767px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#009600;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="199.15128"
y="170.65247"
id="text204"><tspan
sodipodi:role="line"
x="199.15128"
y="170.65247"
id="tspan202"
style="stroke-width:0.26458332"><tspan
x="199.15128"
y="170.65247"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88055563px;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';fill:#009600;fill-opacity:1;stroke-width:0.26458332"
id="tspan200">tls</tspan></tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:3.52777767px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#009600;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="223.09605"
y="158.60213"
id="text216"><tspan
sodipodi:role="line"
x="223.09605"
y="158.60213"
id="tspan214"
style="stroke-width:0.26458332"><tspan
x="223.09605"
y="158.60213"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88055563px;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';fill:#009600;fill-opacity:1;stroke-width:0.26458332"
id="tspan212">4480</tspan></tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:3.52777767px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#009600;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="235.14638"
y="158.60213"
id="text222"><tspan
sodipodi:role="line"
x="235.14638"
y="158.60213"
id="tspan220"
style="stroke-width:0.26458332"><tspan
x="235.14638"
y="158.60213"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88055563px;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';fill:#009600;fill-opacity:1;stroke-width:0.26458332"
id="tspan218">tls</tspan></tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:3.52777767px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#009600;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="222.57213"
y="182.44083"
id="text228"><tspan
sodipodi:role="line"
x="222.57213"
y="182.44083"
id="tspan226"
style="stroke-width:0.26458332"><tspan
x="222.57213"
y="182.44083"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88055563px;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';fill:#009600;fill-opacity:1;stroke-width:0.26458332"
id="tspan224">1022</tspan></tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:3.52777767px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#009600;fill-opacity:1;stroke:none;stroke-width:0.26458332"
x="234.88441"
y="182.44083"
id="text234"><tspan
sodipodi:role="line"
x="234.88441"
y="182.44083"
id="tspan232"
style="stroke-width:0.26458332"><tspan
x="234.88441"
y="182.44083"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:3.88055563px;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';fill:#009600;fill-opacity:1;stroke-width:0.26458332"
id="tspan230">ssh</tspan></tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 32 KiB

+12
View File
@@ -0,0 +1,12 @@
# TODO: c2s does not warn if udp: 1 (instead of 'true')
udp: true;
prefix: "hello";
listen: "localhost:9000";
listen-host: "localhost";
listen-port: "9000";
+1307
View File
File diff suppressed because it is too large. Load diff
+66
View File
@@ -0,0 +1,66 @@
/* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README)
* on Sat Apr 30 09:55:03 2022.
# conf2struct: generate libconf parsers that read to structs
# Copyright (C) 2018-2021 Yves Rutschle
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef C2S_ECHOCFG_H
#define C2S_ECHOCFG_H
#ifdef LIBCONFIG
# include <libconfig.h>
#endif
struct echocfg_listen_item {
char* host;
char* port;
};
struct echocfg_item {
int udp;
char* prefix;
size_t listen_len;
struct echocfg_listen_item* listen;
};
int echocfg_parse_file(
const char* filename,
struct echocfg_item* echocfg,
const char** errmsg);
void echocfg_fprint(
FILE* out,
struct echocfg_item *echocfg,
int depth);
int echocfg_cl_parse(
int argc,
char* argv[],
struct echocfg_item *echocfg);
#endif
+343
View File
@@ -0,0 +1,343 @@
/* echosrv: a simple line echo server with optional prefix adding.
*
* echosrv --listen localhost6:1234 --prefix "ssl: "
*
* This will bind to 1234, and echo every line pre-pending "ssl: ". This is
* used for testing: we create several such servers with different prefixes,
* then we connect test clients that can then check they get the proper data
* back (thus testing that shoveling works both ways) with the correct prefix
* (thus testing it connected to the expected service).
* **/
#define _GNU_SOURCE
#include <sys/types.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <signal.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <pwd.h>
#include <syslog.h>
#include <libgen.h>
#include <getopt.h>
#include <errno.h>
#define cfg sslhcfg
#include "common.h"
#undef cfg
#include "echosrv-conf.h"
/* Added to make the code compilable under CYGWIN
* */
#ifndef SA_NOCLDWAIT
#define SA_NOCLDWAIT 0
#endif
struct echocfg_item cfg;
void check_res_dump(int res, struct addrinfo *addr, char* syscall)
{
char buf[NI_MAXHOST];
if (res == -1) {
if (addr)
fprintf(stderr, "error %s:%s: %s\n",
sprintaddr(buf, sizeof(buf), addr),
syscall,
strerror(errno));
else
fprintf(stderr, "Dying just because\n");
exit(1);
}
}
void start_echo(int fd)
{
int res;
char buffer[1 << 20];
int ret, prefix_len;
int first = 1;
prefix_len = strlen(cfg.prefix);
memset(buffer, 0, sizeof(buffer));
strcpy(buffer, cfg.prefix);
while (1) {
ret = read(fd, buffer + prefix_len, sizeof(buffer) - prefix_len);
if (ret <= 0) {
fprintf(stderr, "%s", strerror(errno));
return;
}
if (first) {
res = write(fd, buffer, ret + prefix_len);
first = 0;
if (write(1, buffer, ret + prefix_len) < 0) {
fprintf(stderr, "%s", strerror(errno));
}
} else {
res = write(fd, buffer + prefix_len, ret);
}
if (res < 0) {
fprintf(stderr, "%s", strerror(errno));
return;
}
}
}
/* TCP echo server: accepts connections to an endpoint, forks an echo for each
* connection, forever. Prefix is added at start of response stream */
void tcp_echo(struct listen_endpoint* listen_socket)
{
while (1) {
int in_socket = accept(listen_socket->socketfd, 0, 0);
if (in_socket == -1) {
perror("tcp_echo:accept");
exit(1);
}
if (!fork())
{
close(listen_socket->socketfd);
start_echo(in_socket);
exit(0);
}
close(in_socket);
waitpid(-1, NULL, WNOHANG);
}
}
void print_udp_xchange(int sockfd, struct sockaddr* addr, socklen_t addrlen)
{
struct addrinfo src_addrinfo, to_addrinfo;
char str_addr[NI_MAXHOST+1+NI_MAXSERV+1];
char str_addr2[NI_MAXHOST+1+NI_MAXSERV+1];
struct sockaddr_storage ss;
src_addrinfo.ai_addr = (struct sockaddr*)&ss;
src_addrinfo.ai_addrlen = sizeof(ss);
getsockname(sockfd, src_addrinfo.ai_addr, &src_addrinfo.ai_addrlen);
to_addrinfo.ai_addr = addr;
to_addrinfo.ai_addrlen = sizeof(*addr);
fprintf(stderr, "UDP local %s remote %s\n",
sprintaddr(str_addr, sizeof(str_addr), &src_addrinfo),
sprintaddr(str_addr2, sizeof(str_addr2), &to_addrinfo)
);
}
/* UDP echo server: receive packets, return them, forever.
* Prefix is added at each packet */
void udp_echo(struct listen_endpoint* listen_socket)
{
char data[65536];
struct sockaddr src_addr;
socklen_t addrlen;
memset(data, 0, sizeof(data));
size_t prefix_len = strlen(cfg.prefix);
memcpy(data, cfg.prefix, prefix_len);
while (1) {
addrlen = sizeof(src_addr);
size_t len = recvfrom(listen_socket->socketfd,
data + prefix_len,
sizeof(data) - prefix_len,
0,
&src_addr,
&addrlen);
if (len < 0) {
perror("recvfrom");
}
*(data + prefix_len + len) = 0;
fprintf(stderr, "%ld: %s\n", len, data + prefix_len);
print_udp_xchange(listen_socket->socketfd, &src_addr, addrlen);
int res = sendto(listen_socket->socketfd,
data,
len + prefix_len,
0,
&src_addr,
addrlen);
if (res < 0) {
perror("sendto");
}
}
}
void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen)
{
int i;
for (i = 0; i < num_addr_listen; i++) {
if (!fork()) {
if (cfg.udp) {
udp_echo(&listen_sockets[i]);
} else {
tcp_echo(&listen_sockets[i]);
}
}
}
wait(NULL);
}
/* Following is a number of utility functions copied from common.c: linking
* against common.o directly means echosrv has to work with sslh config struct,
* which makes it all too awkward */
/* simplified from common.c */
char* sprintaddr(char* buf, size_t size, struct addrinfo *a)
{
char host[NI_MAXHOST], serv[NI_MAXSERV];
int res;
res = getnameinfo(a->ai_addr, a->ai_addrlen,
host, sizeof(host),
serv, sizeof(serv),
0 );
if (res) {
/* Name resolution failed: do it numerically instead */
res = getnameinfo(a->ai_addr, a->ai_addrlen,
host, sizeof(host),
serv, sizeof(serv),
NI_NUMERICHOST | NI_NUMERICSERV);
/* should not fail but... */
if (res) {
strcpy(host, "?");
strcpy(serv, "?");
}
}
snprintf(buf, size, "%s:%s", host, serv);
return buf;
}
/* simplified from common.c */
int listen_single_addr(struct addrinfo* addr, int keepalive, int udp)
{
struct sockaddr_storage *saddr;
int sockfd, one, res;
saddr = (struct sockaddr_storage*)addr->ai_addr;
sockfd = socket(saddr->ss_family, udp ? SOCK_DGRAM : SOCK_STREAM, 0);
check_res_dump(sockfd, addr, "socket");
one = 1;
res = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one));
check_res_dump(res, addr, "setsockopt(SO_REUSEADDR)");
if (addr->ai_addr->sa_family == AF_INET6) {
res = setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&one, sizeof(one));
check_res_dump(res, addr, "setsockopt(IPV6_V6ONLY)");
}
res = bind(sockfd, addr->ai_addr, addr->ai_addrlen);
check_res_dump(res, addr, "bind");
if (!udp) {
res = listen (sockfd, 50);
check_res_dump(res, addr, "listen");
}
return sockfd;
}
/* simplified from common.c */
int resolve_split_name(struct addrinfo **out, char* host, char* serv)
{
struct addrinfo hint;
char *end;
int res;
memset(&hint, 0, sizeof(hint));
hint.ai_family = PF_UNSPEC;
hint.ai_socktype = SOCK_STREAM;
/* If it is a RFC-Compliant IPv6 address ("[1234::12]:443"), remove brackets
* around IP address */
if (host[0] == '[') {
end = strrchr(host, ']');
if (!end) {
fprintf(stderr, "%s: no closing bracket in IPv6 address?\n", host);
return -1;
}
host++; /* skip first bracket */
*end = 0; /* remove last bracket */
}
res = getaddrinfo(host, serv, &hint, out);
if (res)
fprintf(stderr, "%s `%s:%s'\n", gai_strerror(res), host, serv);
return res;
}
int start_listen_sockets(struct listen_endpoint *sockfd[])
{
struct addrinfo *addr, *start_addr;
char buf[NI_MAXHOST];
int i, res;
int num_addr = 0, keepalive = 0, udp = 0;
*sockfd = NULL;
fprintf(stderr, "Listening to:\n");
for (i = 0; i < cfg.listen_len; i++) {
udp = cfg.udp;
res = resolve_split_name(&start_addr, cfg.listen[i].host, cfg.listen[i].port);
if (res) exit(4);
for (addr = start_addr; addr; addr = addr->ai_next) {
num_addr++;
*sockfd = realloc(*sockfd, num_addr * sizeof(*sockfd));
(*sockfd)[num_addr-1].socketfd = listen_single_addr(addr, keepalive, udp);
(*sockfd)[num_addr-1].type = udp ? SOCK_DGRAM : SOCK_STREAM;
fprintf(stderr, "%d:\t%s\n", (*sockfd)[num_addr-1].socketfd, sprintaddr(buf, sizeof(buf), addr));
}
freeaddrinfo(start_addr);
}
return num_addr;
}
int main(int argc, char *argv[])
{
extern char *optarg;
extern int optind;
int num_addr_listen;
struct listen_endpoint *listen_sockets;
memset(&cfg, 0, sizeof(cfg));
if (echocfg_cl_parse(argc, argv, &cfg))
exit(1);
echocfg_fprint(stdout, &cfg, 0);
num_addr_listen = start_listen_sockets(&listen_sockets);
main_loop(listen_sockets, num_addr_listen);
return 0;
}
+37
View File
@@ -0,0 +1,37 @@
# conf2struct for echosrv
header: "echosrv-conf.h";
parser: "echosrv-conf.c";
printer: true;
conffile_option: ("F", "config");
config: {
name: "echocfg",
type: "list",
items: (
{name: "udp", type: "bool"; default: false; },
{name: "prefix", type: "string"; },
{ name: "listen",
type: "list",
items: (
{ name: "host"; type: "string"; var: true; },
{ name: "port"; type: "string"; var: true; }
)
}
)
}
cl_groups: (
{ name: "listen"; pattern: "(.+):(\w+)"; description: "Listen on host:port";
short: "p"; argdesc: "<host:port>";
list: "listen";
# no override, this just adds to the list (and thus can be specified several times)
targets: (
{ path: "host"; value: "$1" },
{ path: "port"; value: "$2" }
);
}
)
+109
View File
@@ -0,0 +1,109 @@
/* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README)
* on Sat Nov 7 09:19:26 2020.
# conf2struct: generate libconf parsers that read to structs
# Copyright (C) 2018-2019 Yves Rutschle
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef C2S_SSLHCFG_H
#define C2S_SSLHCFG_H
#ifdef LIBCONFIG
# include <libconfig.h>
#endif
#include "probe.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
struct sslhcfg_listen_item {
char* host;
char* port;
int keepalive;
};
struct sslhcfg_protocols_item {
char* name;
char* host;
char* port;
int service_is_present;
char* service;
int fork;
int tfo_ok;
int log_level;
int keepalive;
size_t sni_hostnames_len;
char** sni_hostnames;
size_t alpn_protocols_len;
char** alpn_protocols;
size_t regex_patterns_len;
char** regex_patterns;
int minlength_is_present;
int minlength;
T_PROBE* probe;
struct addrinfo* saddr;
void* data;
};
struct sslhcfg_item {
char* prefix;
int verbose;
int foreground;
int inetd;
int numeric;
int transparent;
int timeout;
int user_is_present;
char* user;
int pidfile_is_present;
char* pidfile;
int chroot_is_present;
char* chroot;
char* syslog_facility;
char* on_timeout;
size_t listen_len;
struct sslhcfg_listen_item* listen;
size_t protocols_len;
struct sslhcfg_protocols_item* protocols;
};
int sslhcfg_parse_file(
const char* filename,
struct sslhcfg_item* sslhcfg,
const char** errmsg);
void sslhcfg_fprint(
FILE* out,
struct sslhcfg_item *sslhcfg,
int depth);
int sslhcfg_cl_parse(
int argc,
char* argv[],
struct sslhcfg_item *sslhcfg);
#endif
+153
View File
@@ -0,0 +1,153 @@
# This file is provided as documentation to show what is
# possible. It should not be used as-is, and probably should
# not be used as a starting point for a working
# configuration. Instead use basic.cfg.
foreground: true;
inetd: false;
numeric: false;
transparent: false;
timeout: 2;
user: "nobody";
pidfile: "/var/run/sslh.pid";
chroot: "/var/empty";
# Logging configuration
# Value: 1: stdout; 2: syslog; 3: stdout+syslog; 4: logfile; ...; 7: all
# Defaults are indicated here, and should be sensible. Generally, you want *-error
# to be always enabled, to know if something is going wrong.
verbose-config: 0; # 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: 0; # connection attempts towards targets
verbose-fd: 0; # file descriptor activity, open/close/whatnot
verbose-packets: 0; # hexdump packets on which probing is done
verbose-probe-info: 0; # 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
# Specify a path to the logfile.
#logfile: "/var/log/sslh.log"
# Specify the number of concurrent UDP connection that can
# be managed (default 1024)
udp_max_connections: 16;
# Specify which syslog facility to use (names for your
# system are usually defined in /usr/include/*/sys/syslog.h
# or equivalent)
# Default is "auth"
# "none" disables use of syslog
syslog_facility: "auth";
# List of interfaces on which we should listen
# Options:
listen:
(
{ host: "thelonious"; port: "443"; },
{ host: "thelonious"; port: "8080"; keepalive: true; },
{ host: "thelonious"; is_udp: true; port: "443" }
);
# List of protocols
#
# Each protocol entry consists of:
# name: name of the probe. These are listed on the command
# line (ssh -?), plus 'regex' and 'timeout'.
# service: (optional) libwrap service name (see hosts_access(5))
# host, port: where to connect when this probe succeeds
# log_level: 0 to turn off logging
# 1 to log each incoming connection
# keepalive: Should TCP keepalive be on or off for that
# connection (default is off)
# fork: Should a new process be forked for this protocol?
# (only useful for sslh-select)
# tfo_ok: Set to true if the server supports TCP FAST OPEN
# resolve_on_forward: Set to true if server address should be resolved on
# (every) newly incoming connection (again)
# transparent: Set to true to proxy this protocol
# transparently (server sees the remote client IP
# address). Same as the global option, but per-protocol
#
# Probe-specific options:
# (sslh will try each probe in order they are declared, and
# connect to the first that matches.)
#
# tls:
# sni_hostnames: list of FQDN for that target
# 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
#
# if both sni_hostnames AND alpn_protocols are specified, both must match
#
# if neither are set, it is just checked whether this is the TLS protocol or not
#
# Obviously set the most specific probes
# first, and if you use TLS with no ALPN/SNI
# set it as the last TLS probe
# regex:
# regex_patterns: list of patterns to match for
# that target.
#
# You can specify several of 'regex' and 'tls'.
protocols:
(
{ name: "ssh"; service: "ssh"; host: "localhost"; port: "22";
keepalive: true; fork: true; tfo_ok: true },
{ name: "http"; host: "localhost"; port: "80"; },
# match BOTH ALPN/SNI
{ name: "tls"; host: "localhost"; port: "5223"; alpn_protocols: [ "xmpp-client" ]; sni_hostnames: [ "im.somethingelse.net" ]; log_level: 0; tfo_ok: true },
# just match ALPN
{ name: "tls"; host: "localhost"; port: "443"; alpn_protocols: [ "h2", "http/1.1", "spdy/1", "spdy/2", "spdy/3" ]; log_level: 0; tfo_ok: true },
{ name: "tls"; host: "localhost"; port: "xmpp-client"; alpn_protocols: [ "xmpp-client" ]; log_level: 0; tfo_ok: true },
# just match SNI
{ name: "tls"; host: "localhost"; port: "993"; sni_hostnames: [ "mail.rutschle.net", "mail.englishintoulouse.com" ]; log_level: 0; tfo_ok: true },
{ name: "tls"; host: "localhost"; port: "xmpp-client"; sni_hostnames: [ "im.rutschle.net", "im.englishintoulouse.com" ]; log_level: 0; tfo_ok: true },
# Let's Encrypt (tls-alpn-* challenges)
{ name: "tls"; host: "localhost"; port: "letsencrypt-client"; alpn_protocols: [ "acme-tls/1" ]; log_level: 0;},
# catch anything else TLS
{ name: "tls"; host: "localhost"; port: "443"; tfo_ok: true },
# Forward UDP
{ name: "regex"; host: "localhost"; is_udp: true; port: "123";
udp_timeout: 20; # Time after which the "connection" is forgotten
regex_patterns: [ "hello" ]; },
# Forward Teamspeak3 (Voice only)
{ name: "teamspeak"; host: "localhost"; is_udp: true; port: "9987"; },
# Forward IETF QUIC-50 ("Q050" -> "\x51\x30\x35\x30")
# Remember that the regex needs to be adjusted for every supported QUIC version.
{ name: "regex"; host: "localhost"; is_udp: true; port: "4433"; regex_patterns: [ "\x51\x30\x35\x30" ]; },
# Regex examples -- better use the built-in probes for real-world use!
# OpenVPN
{ name: "regex"; host: "localhost"; port: "1194"; regex_patterns: [ "^\x00[\x0D-\xFF]$", "^\x00[\x0D-\xFF]\x38" ]; },
# Jabber
{ name: "regex"; host: "localhost"; port: "5222"; regex_patterns: [ "jabber" ];
minlength: 60; # Won't even try to match the regex if we don't have that many bytes
},
# Catch-all (but better use 'anyprot')
{ name: "regex"; host: "localhost"; port: "443"; regex_patterns: [ "" ]; },
# Where to connect in case of timeout (defaults to ssh)
{ name: "timeout"; service: "daytime"; host: "localhost"; port: "daytime"; }
);
# Optionally, specify to which protocol to connect in case
# of timeout (defaults to "ssh").
# You can timeout to any arbitrary address by setting an
# entry in 'protocols' named "timeout".
# This enables you to set a tcpd service name for this
# protocol too.
on-timeout: "timeout";
+111
View File
@@ -0,0 +1,111 @@
/*
gap.c: gap, a simple, dynamically-growing array
of pointers that never shrinks
# Copyright (C) 2021 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 <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include "sslh-conf.h"
#include "gap.h"
/* Allocate one page-worth of elements */
static int gap_len_alloc(int elem_size)
{
return getpagesize() / elem_size;
}
/* Creates a new gap at least `len` big, all pointers are initialised at NULL */
gap_array* gap_init(int len)
{
gap_array* gap = malloc(sizeof(*gap));
if (!gap) return NULL;
memset(gap, 0, sizeof(*gap));
int elem_size = sizeof(gap->array[0]);
gap->len = gap_len_alloc(elem_size);
if (gap->len < len) gap->len = len;
gap->array = malloc(gap->len * elem_size);
if (!gap->array) {
free(gap);
return NULL;
}
for (int i = 0; i < gap->len; i++)
gap->array[i] = NULL;
return gap;
}
int gap_extend(gap_array* gap)
{
int elem_size = sizeof(gap->array[0]);
int new_length = gap->len + gap_len_alloc(elem_size);
void** new = realloc(gap->array, new_length * elem_size);
if (!new) return -1;
gap->array = new;
for (int i = gap->len; i < new_length; i++) {
gap->array[i] = NULL;
}
gap->len = new_length;
return 0;
}
void gap_destroy(gap_array* gap)
{
free(gap->array);
free(gap);
}
/* In gap, find element pointing to ptr, then shift the rest of the array that
* is considered len elements long.
* A poor man's list, if you will. Currently only used to remove probing
* connections, so it only copies a few pointers at most.
* Returns -1 if ptr was not found */
int gap_remove_ptr(gap_array* gap, void* ptr, int len)
{
int start, i;
for (i = 0; i < len; i++)
if (gap->array[i] == ptr)
break;
if (i < len)
start = i;
else
return -1;
for (i = start; i < len - 1; i++) {
gap->array[i] = gap->array[i+1];
}
return 0;
}
+43
View File
@@ -0,0 +1,43 @@
#ifndef GAP_H
#define GAP_H
typedef struct gap_array gap_array;
gap_array* gap_init(int len);
static void* gap_get(gap_array* gap, int index);
static int gap_set(gap_array* gap, int index, void* ptr);
void gap_destroy(gap_array* gap);
int gap_remove_ptr(gap_array* gap, void* ptr, int len);
/* Private declarations to allow inlining.
* Don't assume my implementation. */
typedef struct gap_array {
int len; /* Number of elements in array */
void** array;
} gap_array;
int gap_extend(gap_array* gap);
static inline int __attribute__((unused)) gap_set(gap_array* gap, int index, void* ptr)
{
while (index >= gap->len) {
int res = gap_extend(gap);
if (res == -1) return -1;
}
gap->array[index] = ptr;
return 0;
}
static inline void* __attribute__((unused)) gap_get(gap_array* gap, int index)
{
/* sslh-ev routinely reads before it writes. It's not clear if it should be
* its job to check the length (and add a gap_getlen()), or if it should be
* gap_get()'s job. This will do for now */
if (index >= gap->len) return NULL;
return gap->array[index];
}
#endif
Executable
+49
View File
@@ -0,0 +1,49 @@
#! /bin/sh
if [ ${#} -eq 1 ] && [ "x$1" = "x-r" ]; then
# release text only
QUIET=1
else
QUIET=0
fi
if [ ! -d .git ] || ! `(git status | grep -q "On branch") 2> /dev/null`; then
# If we don't have git, we can't work out what
# version this is. It must have been downloaded as a
# zip file.
# If downloaded from the release page, the directory
# has the version number.
release=`pwd | sed s/.*sslh-// | grep "[[:digit:]]"`
if [ "x$release" = "x" ]; then
# If downloaded from the head, GitHub creates the
# zip file with all files dated from the last
# change: use the Makefile's modification time as a
# release number
release=head-`perl -MPOSIX -e 'print strftime "%Y-%m-%d",localtime((stat "Makefile")[9])'`
fi
fi
if [ -d .git ] && head=`git rev-parse --verify HEAD 2>/dev/null`; then
# generate the version info based on the tag
release=`(git describe --tags || git --describe || git describe --all --long) \
2>/dev/null | tr -s '/' '-' | tr -d '\n'`
# Are there uncommitted changes?
git update-index --refresh --unmerged > /dev/null
if git diff-index --name-only HEAD | grep -v "^scripts/package" \
| read dummy; then
release="$release-dirty"
fi
fi
if [ $QUIET -ne 1 ]; then
printf "#ifndef VERSION_H \n"
printf "#define VERSION_H \n\n"
printf "#define VERSION \"$release\"\n"
printf "#endif\n"
else
printf "$release\n"
fi
+224
View File
@@ -0,0 +1,224 @@
/*
* a fixed-sized hash
*
# Copyright (C) 2022 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
#
# */
/* * The hash is open-addressing, linear search, robin-hood insertion, with
* backward shift deletion. References:
* https://codecapsule.com/2013/11/11/robin-hood-hashing/
* https://codecapsule.com/2013/11/17/robin-hood-hashing-backward-shift-deletion/
* This means items are reordered upon insertion and deletion, and the hash
* is well-ordered at all times with no tombstones.
*
* Each pointer is either:
* - to a connection struct
* - FREE (NULL) if not allocated
*
* */
#include <stdlib.h>
#include <stddef.h>
#include "gap.h"
typedef void* hash_item;
#include "hash.h"
static void* const FREE = NULL;
struct hash {
int hash_size; /* Max number of items in the hash */
int item_cnt; /* Number of items in the hash */
gap_array* data;
hash_make_key_fn hash_make_key;
hash_cmp_item_fn cmp_item;
};
typedef struct hash hash;
static int hash_make_key(hash* h, hash_item item)
{
return h->hash_make_key(item) % h->hash_size;
}
hash* hash_init(int hash_size, hash_make_key_fn make_key, hash_cmp_item_fn cmp_item)
{
hash* h = malloc(sizeof(*h));
if (!h) return NULL;
h->hash_size = hash_size;
h->item_cnt = 0;
h->data = gap_init(hash_size);
h->hash_make_key = make_key;
h->cmp_item = cmp_item;
return h;
}
/* Return the index following i in h */
static int hash_next_index(hash* h, int i)
{
return (i + 1) % h->hash_size;
}
/* Returns the index in h of specified address, -1 if not found
* item is an item object that must return the target wanted index and for
* which comparison with the searched object will succeed.
* */
static int hash_find_index(hash* h, hash_item item)
{
hash_item cnx;
int index = hash_make_key(h, item);
int cnt = 0;
cnx = gap_get(h->data, index);
#ifdef DEBUG
fprintf(stderr, "searching %d\n", index);
#endif
while (cnx != FREE) {
if (cnt++ > h->hash_size) return -1;
if (!h->cmp_item(cnx, item))
break;
index = hash_next_index(h, index);
cnx = gap_get(h->data, index);
#ifdef DEBUG
fprintf(stderr, "searching %d\n", index);
#endif
}
if (cnx == FREE) return -1;
return index;
}
hash_item hash_find(hash* h, hash_item item)
{
int index = hash_find_index(h, item);
if (index == -1) return NULL;
hash_item out = gap_get(h->data, index);
return out;
}
/* Returns DIB: distance to initial bucket */
static int distance(int current_index, hash* h, hash_item item)
{
int wanted_index = hash_make_key(h, item);
if (wanted_index <= current_index)
return current_index - wanted_index;
else
return current_index - wanted_index + h->hash_size;
}
int hash_insert(hash* h, hash_item new)
{
int bubble_wanted_index = hash_make_key(h, new);
int index = bubble_wanted_index;
gap_array* hash = h->data;
if (h->item_cnt == h->hash_size)
return -1;
hash_item curr_item = gap_get(hash, index);
while (curr_item) {
if (distance(index, h, curr_item) < distance(index, h, new)) {
gap_set(h->data, index, new);
#if DEBUG
fprintf(stderr, "intermediate insert [%s] at %d\n", &new->client_addr, index);
#endif
new = curr_item;
}
index = hash_next_index(h, index);
curr_item = gap_get(hash, index);
}
#if DEBUG
fprintf(stderr, "final insert at %d\n", index);
#endif
gap_set(hash, index, new);
h->item_cnt++;
return 0;
}
/* Remove cnx from the hash */
int hash_remove(hash* h, hash_item item)
{
gap_array* hash = h->data;
int index = hash_find_index(h, item);
if (index == -1) return -1; /* Tried to remove something that isn't there */
while (1) {
int next_index = hash_next_index(h, index);
hash_item next = gap_get(h->data, next_index);
if ((next == FREE) || (distance(next_index, h, next) == 0)) {
h->item_cnt--;
gap_set(hash, index, FREE);
return 0;
}
gap_set(hash, index, next);
index = hash_next_index(h, index);;
}
return 0;
}
#if HASH_TESTING
#include <stdio.h>
#include <string.h>
#define STR_LENGTH 16
struct hash_item {
int wanted_index;
char str[STR_LENGTH];
};
void hash_dump(hash* h, char* filename)
{
char str[STR_LENGTH];
FILE* out = fopen(filename, "w");
if (!out) {
perror(filename);
exit(1);
}
fprintf(out, "<hash elem=%d>\n", h->item_cnt);
for (int i = 0; i < h->hash_size; i++) {
hash_item item = gap_get(h->data, i);
int idx = 0;
memset(str, 0, STR_LENGTH);
if (item) {
idx = hash_make_key(h, item);
memcpy(str, ((struct hash_item*)item)->str, STR_LENGTH);
}
fprintf(out, "\t%d:%d:%s\n", i, idx, str);
}
fprintf(out, "</hash>\n");
fclose(out);
}
#endif
+28
View File
@@ -0,0 +1,28 @@
#ifndef HASH_H
#define HASH_H
/* You will need to typedef a pointer type to hash_item before including this
* .h */
typedef struct hash hash;
/* Function that returns a key (index) for a given item. The key must be always
* the same for an item. It doesn't need to be bounded (hash.c masks it for you) */
typedef int (*hash_make_key_fn)(hash_item item);
/* Function that compares two items: returns 0 if they are the same */
typedef int (*hash_cmp_item_fn)(hash_item item1, hash_item item2);
hash* hash_init(int hash_size, hash_make_key_fn make_key, hash_cmp_item_fn cmp_item);
int hash_insert(hash* h, hash_item new);
int hash_remove(hash* h, hash_item item);
/* Returns the hash item that matches specification (meaning the
* comparison function returns true for cmp(x, item), or NULL if not found */
hash_item hash_find(hash* h, hash_item item);
void hash_dump(hash* h, char* filename); /* For development only */
#endif
+7
View File
@@ -0,0 +1,7 @@
CFLAGS=-DHASH_TESTING -O2 -Wall
OBJ=../hash.o ../gap.o htest.o
htest: $(OBJ)
$(CC) -o htest $(OBJ)
+8
View File
@@ -0,0 +1,8 @@
# Basic delete
a 10 aa
a 10 ab
a 10 ac
a 20 ba
a 21 bb
d 21 bb
+34
View File
@@ -0,0 +1,34 @@
<hash elem=4>
0:0:
1:0:
2:0:
3:0:
4:0:
5:0:
6:0:
7:0:
8:0:
9:0:
10:10:aa
11:10:ab
12:10:ac
13:0:
14:0:
15:0:
16:0:
17:0:
18:0:
19:0:
20:20:ba
21:0:
22:0:
23:0:
24:0:
25:0:
26:0:
27:0:
28:0:
29:0:
30:0:
31:0:
</hash>
+9
View File
@@ -0,0 +1,9 @@
# Delete inside a block with nothing after
a 10 aa
a 10 ab
a 12 ac
a 13 ad
a 14 ae
d 14 ae
+34
View File
@@ -0,0 +1,34 @@
<hash elem=4>
0:0:
1:0:
2:0:
3:0:
4:0:
5:0:
6:0:
7:0:
8:0:
9:0:
10:10:aa
11:10:ab
12:12:ac
13:13:ad
14:0:
15:0:
16:0:
17:0:
18:0:
19:0:
20:0:
21:0:
22:0:
23:0:
24:0:
25:0:
26:0:
27:0:
28:0:
29:0:
30:0:
31:0:
</hash>
+9
View File
@@ -0,0 +1,9 @@
# wrap-around and delete below floor
a 2 ba
a 30 aa
a 30 ab
a 30 ac
a 30 ad
a 2 bb
d 30 ab
+34
View File
@@ -0,0 +1,34 @@
<hash elem=5>
0:30:ad
1:0:
2:2:ba
3:2:bb
4:0:
5:0:
6:0:
7:0:
8:0:
9:0:
10:0:
11:0:
12:0:
13:0:
14:0:
15:0:
16:0:
17:0:
18:0:
19:0:
20:0:
21:0:
22:0:
23:0:
24:0:
25:0:
26:0:
27:0:
28:0:
29:0:
30:30:aa
31:30:ac
</hash>
+10
View File
@@ -0,0 +1,10 @@
# delete in a discontinuous block
a 10 aa
a 11 ab
a 12 ac
a 14 ad
a 10 bc
d 11 ab
+34
View File
@@ -0,0 +1,34 @@
<hash elem=4>
0:0:
1:0:
2:0:
3:0:
4:0:
5:0:
6:0:
7:0:
8:0:
9:0:
10:10:aa
11:10:bc
12:12:ac
13:0:
14:14:ad
15:0:
16:0:
17:0:
18:0:
19:0:
20:0:
21:0:
22:0:
23:0:
24:0:
25:0:
26:0:
27:0:
28:0:
29:0:
30:0:
31:0:
</hash>
+11
View File
@@ -0,0 +1,11 @@
# Delete an unexisting element. And on an empty hash
a 10 aa
d 10 ab
d 12 bc
# Empty for real
d 10 aa
d 10 aa
+34
View File
@@ -0,0 +1,34 @@
<hash elem=0>
0:0:
1:0:
2:0:
3:0:
4:0:
5:0:
6:0:
7:0:
8:0:
9:0:
10:0:
11:0:
12:0:
13:0:
14:0:
15:0:
16:0:
17:0:
18:0:
19:0:
20:0:
21:0:
22:0:
23:0:
24:0:
25:0:
26:0:
27:0:
28:0:
29:0:
30:0:
31:0:
</hash>
+39
View File
@@ -0,0 +1,39 @@
# delete on a full hash
# First, fill the hash :-)
a 0 aa
a 1 ab
a 2 ac
a 3 ad
a 4 ae
a 5 af
a 6 ag
a 7 ah
a 8 ai
a 9 af
a 10 ba
a 11 bb
a 12 bc
a 13 bd
a 14 be
a 15 bf
a 16 bg
a 17 bh
a 18 bi
a 19 bj
a 20 ca
a 21 cb
a 22 cd
a 23 ce
a 24 cf
a 25 cg
a 26 ch
a 27 ci
a 28 cj
a 29 ck
a 30 da
a 31 db
d 21 cb
+34
View File
@@ -0,0 +1,34 @@
<hash elem=31>
0:0:aa
1:1:ab
2:2:ac
3:3:ad
4:4:ae
5:5:af
6:6:ag
7:7:ah
8:8:ai
9:9:af
10:10:ba
11:11:bb
12:12:bc
13:13:bd
14:14:be
15:15:bf
16:16:bg
17:17:bh
18:18:bi
19:19:bj
20:20:ca
21:0:
22:22:cd
23:23:ce
24:24:cf
25:25:cg
26:26:ch
27:27:ci
28:28:cj
29:29:ck
30:30:da
31:31:db
</hash>
+10
View File
@@ -0,0 +1,10 @@
# Delete inside a block with something discontinuous
a 10 aa
a 10 ab
a 12 ac
a 13 ad
a 14 ae
# ab shifts, ac and next doesn't
d 10 aa
+34
View File
@@ -0,0 +1,34 @@
<hash elem=4>
0:0:
1:0:
2:0:
3:0:
4:0:
5:0:
6:0:
7:0:
8:0:
9:0:
10:10:ab
11:0:
12:12:ac
13:13:ad
14:14:ae
15:0:
16:0:
17:0:
18:0:
19:0:
20:0:
21:0:
22:0:
23:0:
24:0:
25:0:
26:0:
27:0:
28:0:
29:0:
30:0:
31:0:
</hash>
+8
View File
@@ -0,0 +1,8 @@
# Basic delete when wrapping, between wrap and floor
a 30 aa
a 30 ab
a 30 ac
a 30 ba
a 30 bb
d 30 ac
+34
View File
@@ -0,0 +1,34 @@
<hash elem=4>
0:30:ba
1:30:bb
2:0:
3:0:
4:0:
5:0:
6:0:
7:0:
8:0:
9:0:
10:0:
11:0:
12:0:
13:0:
14:0:
15:0:
16:0:
17:0:
18:0:
19:0:
20:0:
21:0:
22:0:
23:0:
24:0:
25:0:
26:0:
27:0:
28:0:
29:0:
30:30:aa
31:30:ab
</hash>
+10
View File
@@ -0,0 +1,10 @@
# Delete inside a block with wrapping, with something after
a 30 aa
a 30 ab
a 30 ac
a 1 ad
a 3 ae
# shift ad but not ae
d 14 ae
+34
View File
@@ -0,0 +1,34 @@
<hash elem=5>
0:30:ac
1:1:ad
2:0:
3:3:ae
4:0:
5:0:
6:0:
7:0:
8:0:
9:0:
10:0:
11:0:
12:0:
13:0:
14:0:
15:0:
16:0:
17:0:
18:0:
19:0:
20:0:
21:0:
22:0:
23:0:
24:0:
25:0:
26:0:
27:0:
28:0:
29:0:
30:30:aa
31:30:ab
</hash>
+8
View File
@@ -0,0 +1,8 @@
# delete before wrap
a 30 aa
a 30 ab
a 30 ac
a 30 ad
# shift ac and ad
d 30 ab
+34
View File
@@ -0,0 +1,34 @@
<hash elem=3>
0:30:ad
1:0:
2:0:
3:0:
4:0:
5:0:
6:0:
7:0:
8:0:
9:0:
10:0:
11:0:
12:0:
13:0:
14:0:
15:0:
16:0:
17:0:
18:0:
19:0:
20:0:
21:0:
22:0:
23:0:
24:0:
25:0:
26:0:
27:0:
28:0:
29:0:
30:30:aa
31:30:ac
</hash>
+11
View File
@@ -0,0 +1,11 @@
# Delete with wrapping in discontinuous group
a 30 aa
a 30 ab
a 30 ac
a 31 ad
a 2 ba
a 3 bb
# shift ac and ad but not ba and bb
d 30 ab
+34
View File
@@ -0,0 +1,34 @@
<hash elem=5>
0:31:ad
1:0:
2:2:ba
3:3:bb
4:0:
5:0:
6:0:
7:0:
8:0:
9:0:
10:0:
11:0:
12:0:
13:0:
14:0:
15:0:
16:0:
17:0:
18:0:
19:0:
20:0:
21:0:
22:0:
23:0:
24:0:
25:0:
26:0:
27:0:
28:0:
29:0:
30:30:aa
31:30:ac
</hash>
Executable
BIN
View File
Binary file not shown.
+109
View File
@@ -0,0 +1,109 @@
/* Wee testing program from the hash code:
* htest <script> <dump>
*
* scripts are a list of operations:
* a $index $string
* => add an element at specified index
* d $index $string
* => remove an element
* s $index $string
* => prints the actual element index, if it's there
*
* The hash is dumped to the dump file at each iteration.
*/
#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
/* tests have been written for a hash that holds 32 items */
#define HASH_SIZE 32
#define STR_LENGTH 16
struct hash_item {
int wanted_index;
char str[STR_LENGTH];
};
typedef struct hash_item* hash_item;
#include "../hash.h"
static int cmp_item(hash_item item1, hash_item item2)
{
return strcmp(item1->str, item2->str);
}
static int hash_make_key(hash_item item)
{
return item->wanted_index;
}
static void htest_next_key(FILE* f, char* action, int* key, char str[STR_LENGTH])
{
int res = 0;
while ((res != 3) && (res != EOF))
res = fscanf(f, "%c %d %s\n", action, key, str);
if (res == EOF) exit(0);
}
int main(int argc, char* argv[])
{
hash* h = hash_init(HASH_SIZE, &hash_make_key, &cmp_item);
char action;
hash_item item;
int line = 0;
FILE* f;
if (argc != 3) {
fprintf(stderr, "Usage: htest <script file> <dump file>\n");
exit(1);
}
char* script_file = argv[1];
char* dump_file = argv[2];
f = fopen(argv[1], "r");
if (!f) {
perror(script_file);
exit(1);
}
while (1) {
item= malloc(sizeof(*item));
action = ' ';
line++;
htest_next_key(f, &action, &item->wanted_index, item->str);
fprintf(stderr, "action %d: %c %d %s\n", line, action, item->wanted_index, item->str);
switch (action) {
case 'a': /* add */
fprintf(stderr, "inserting [%s] at %d\n", item->str, item->wanted_index);
hash_insert(h, item);
break;
case 'd': /* del */
fprintf(stderr, "removing [%s] at %d\n", item->str, item->wanted_index);
hash_remove(h, item);
break;
case 's': /* search */
fprintf(stderr, "searching\n");
struct hash_item* found = hash_find(h, item);
fprintf(stderr, "searching %d[%s]: %p\n", item->wanted_index, item->str, found);
break;
case 'q': /* quit */
exit(1);
}
hash_dump(h, dump_file);
}
return 0;
}
+6
View File
@@ -0,0 +1,6 @@
# Basic insertions
a 10 aa
a 10 ab
a 10 ac
a 20 ba
a 21 bb
+34
View File
@@ -0,0 +1,34 @@
<hash elem=5>
0:0:
1:0:
2:0:
3:0:
4:0:
5:0:
6:0:
7:0:
8:0:
9:0:
10:10:aa
11:10:ab
12:10:ac
13:0:
14:0:
15:0:
16:0:
17:0:
18:0:
19:0:
20:20:ba
21:21:bb
22:0:
23:0:
24:0:
25:0:
26:0:
27:0:
28:0:
29:0:
30:0:
31:0:
</hash>
+8
View File
@@ -0,0 +1,8 @@
# insert and bubble with single empty space
a 10 aa
a 11 ab
a 12 ac
a 14 ad
a 10 bc
+34
View File
@@ -0,0 +1,34 @@
<hash elem=5>
0:0:
1:0:
2:0:
3:0:
4:0:
5:0:
6:0:
7:0:
8:0:
9:0:
10:10:aa
11:10:bc
12:11:ab
13:12:ac
14:14:ad
15:0:
16:0:
17:0:
18:0:
19:0:
20:0:
21:0:
22:0:
23:0:
24:0:
25:0:
26:0:
27:0:
28:0:
29:0:
30:0:
31:0:
</hash>
+40
View File
@@ -0,0 +1,40 @@
# Insert on a full hash
# First, fill the hash :-)
a 0 aa
a 1 ab
a 2 ac
a 3 ad
a 4 ae
a 5 af
a 6 ag
a 7 ah
a 8 ai
a 9 af
a 10 ba
a 11 bb
a 12 bc
a 13 bd
a 14 be
a 15 bf
a 16 bg
a 17 bh
a 18 bi
a 19 bj
a 20 ca
a 21 cb
a 22 cd
a 23 ce
a 24 cf
a 25 cg
a 26 ch
a 27 ci
a 28 cj
a 29 ck
a 30 da
a 31 db
# it's full!
a 20 zz
a 31 za
+34
View File
@@ -0,0 +1,34 @@
<hash elem=32>
0:0:aa
1:1:ab
2:2:ac
3:3:ad
4:4:ae
5:5:af
6:6:ag
7:7:ah
8:8:ai
9:9:af
10:10:ba
11:11:bb
12:12:bc
13:13:bd
14:14:be
15:15:bf
16:16:bg
17:17:bh
18:18:bi
19:19:bj
20:20:ca
21:21:cb
22:22:cd
23:23:ce
24:24:cf
25:25:cg
26:26:ch
27:27:ci
28:28:cj
29:29:ck
30:30:da
31:31:db
</hash>
+7
View File
@@ -0,0 +1,7 @@
# wrap-around and insert at full floor
a 2 ba
a 30 aa
a 30 ab
a 30 ac
a 30 ad
a 2 bb
+34
View File
@@ -0,0 +1,34 @@
<hash elem=6>
0:30:ac
1:30:ad
2:2:ba
3:2:bb
4:0:
5:0:
6:0:
7:0:
8:0:
9:0:
10:0:
11:0:
12:0:
13:0:
14:0:
15:0:
16:0:
17:0:
18:0:
19:0:
20:0:
21:0:
22:0:
23:0:
24:0:
25:0:
26:0:
27:0:
28:0:
29:0:
30:30:aa
31:30:ab
</hash>
+7
View File
@@ -0,0 +1,7 @@
# wrap-around and insert above floor
a 30 aa
a 30 ab
a 30 ac
a 30 ad
a 0 ba
a 0 bb
+34
View File
@@ -0,0 +1,34 @@
<hash elem=6>
0:30:ac
1:30:ad
2:0:ba
3:0:bb
4:0:
5:0:
6:0:
7:0:
8:0:
9:0:
10:0:
11:0:
12:0:
13:0:
14:0:
15:0:
16:0:
17:0:
18:0:
19:0:
20:0:
21:0:
22:0:
23:0:
24:0:
25:0:
26:0:
27:0:
28:0:
29:0:
30:30:aa
31:30:ab
</hash>
+41
View File
@@ -0,0 +1,41 @@
#! /usr/bin/perl
# Creates a script of random accesses and deletes
use strict;
my $i = 0;
sub mkstr {
$i++;
return chr(ord('a') + ($i / 26) % 26) . chr(ord('a') + $i % 26);
}
my @elems;
sub add_elem {
my $val = int(rand(32));
my $str = mkstr($val);
push @elems, "$val $str";
print "a $val $str\n";
}
sub del_elem {
my $remove = splice(@elems, rand @elems, 1);
print "d $remove\n";
}
while (1) {
if (@elems < 5) {
add_elem;
} elsif (@elems > 28) {
del_elem;
} else {
if (rand() < .5) {
add_elem;
} else {
del_elem;
}
}
}
Executable
+30
View File
@@ -0,0 +1,30 @@
#! /usr/bin/perl -w
# This runs all the tests.
# Tests scripts are in *.tst files.
# Corresponding output is put in *.out.
# Reference output is put in *.ref.
# Any discrepancy will be reported!
use strict;
my @res;
foreach my $fn (`ls *.tst`) {
chomp $fn;
my $cmd = "./htest $fn $fn.out";
print "$cmd\n";
`$cmd`;
my $res = system("diff -u $fn.ref $fn.out");
push @res, [$fn, ($res == 0 ? "OK" : "*KO*")];
}
format =
@<<<<<<<<<<<<<<<<<<<<<<<<<< @>>>
$_->[0], $_->[1]
.
#format_name STDOUT test_result;
map { write; } @res;
+211
View File
@@ -0,0 +1,211 @@
/*
# log: processing of all outgoing messages
#
# Copyright (C) 2007-2021 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
*/
#define SYSLOG_NAMES
#define _GNU_SOURCE
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include "sslh-conf.h"
#include "common.h"
#include "log.h"
msg_info msg_config = {
LOG_INFO,
&cfg.verbose_config
};
msg_info msg_config_error = {
LOG_ERR,
&cfg.verbose_config_error
};
msg_info msg_fd = {
LOG_DEBUG,
&cfg.verbose_fd
};
/* Internal errors: inconsistent states, impossible values, things that should never happen, and are therefore the sign of memory corruption: hence the LOG_CRIT */
msg_info msg_int_error = {
LOG_CRIT,
&cfg.verbose_system_error
};
/* System errors: when the system around us fails us: memory allocation, fork, ... */
msg_info msg_system_error = {
LOG_ERR,
&cfg.verbose_system_error
};
msg_info msg_packets = {
LOG_INFO,
&cfg.verbose_packets
};
/* additional info when attempting outgoing connections */
msg_info msg_connections_try = {
LOG_DEBUG,
&cfg.verbose_connections_try
};
/* Connection information and failures (e.g. forbidden by policy) */
msg_info msg_connections = {
LOG_INFO,
&cfg.verbose_connections
};
/* Connection failures, e.g. target server not present */
msg_info msg_connections_error = {
LOG_ERR,
&cfg.verbose_connections_error
};
/* comment the probing process */
msg_info msg_probe_info = {
LOG_INFO,
&cfg.verbose_probe_info
};
/* probing errors, e.g. inconsistent data in connections */
msg_info msg_probe_error = {
LOG_ERR,
&cfg.verbose_probe_error
};
/* Bitmasks in verbose-* values */
#define MSG_STDOUT 1
#define MSG_SYSLOG 2
#define MSG_FILE 4
static FILE* logfile_fp = NULL;
/* Prints a message to stderr and/or syslog if appropriate */
void print_message(msg_info info, const char* str, ...)
{
va_list ap;
if ((*info.verbose & MSG_STDOUT) && ! cfg.inetd) {
va_start(ap, str);
vfprintf(stderr, str, ap);
va_end(ap);
}
if (*info.verbose & MSG_SYSLOG) {
va_start(ap, str);
vsyslog(info.log_level, str, ap);
va_end(ap);
}
if (*info.verbose & MSG_FILE && logfile_fp != NULL) {
va_start(ap, str);
vfprintf(logfile_fp, str, ap);
fflush(logfile_fp);
va_end(ap);
}
}
static int do_syslog = 1; /* Should we syslog? controled by syslog_facility = "none" */
/* Open syslog connection with appropriate banner;
* banner is made up of basename(bin_name)+"[pid]" */
void setup_syslog(const char* bin_name) {
char *name1, *name2;
int res, fn;
if (!strcmp(cfg.syslog_facility, "none")) {
do_syslog = 0;
return;
}
name1 = strdup(bin_name);
res = asprintf(&name2, "%s[%d]", basename(name1), getpid());
CHECK_RES_DIE(res, "asprintf");
for (fn = 0; facilitynames[fn].c_val != -1; fn++)
if (strcmp(facilitynames[fn].c_name, cfg.syslog_facility) == 0)
break;
if (facilitynames[fn].c_val == -1) {
fprintf(stderr, "Unknown facility %s\n", cfg.syslog_facility);
exit(1);
}
openlog(name2, LOG_CONS, facilitynames[fn].c_val);
free(name1);
/* Don't free name2, as openlog(3) uses it (at least in glibc) */
}
void setup_logfile()
{
if (cfg.logfile == NULL)
{
return;
}
logfile_fp = fopen(cfg.logfile, "a");
if (logfile_fp == NULL)
{
fprintf(stderr, "Could not open logfile %s for writing: %s\n", cfg.logfile, strerror(errno));
exit(1);
}
}
void close_logfile()
{
if (logfile_fp != NULL)
{
fclose(logfile_fp);
logfile_fp = NULL;
}
}
/* syslogs who connected to where
* desc: string description of the connection. if NULL, log_connection will
* manage on its own
* cnx: connection descriptor
* */
void log_connection(struct connection_desc* desc, const struct connection *cnx)
{
struct connection_desc d;
if (cnx->proto->log_level < 1)
return;
if (!desc) {
desc = &d;
if (!get_connection_desc(desc, cnx)) {
print_message(msg_connections, "%s: lost incoming connection\n",
cnx->proto->name);
return;
}
}
print_message(msg_connections, "%s:connection from %s to %s forwarded from %s to %s\n",
cnx->proto->name,
desc->peer,
desc->service,
desc->local,
desc->target);
}
+36
View File
@@ -0,0 +1,36 @@
#ifndef LOG_H
#define LOG_H
#include "common.h"
void setup_syslog(const char* bin_name);
void setup_logfile();
void close_logfile();
void log_connection(struct connection_desc* desc, const struct connection *cnx);
typedef struct s_msg_info{
int log_level;
int *verbose;
} msg_info;
void print_message(msg_info info, const char* str, ...);
extern msg_info msg_config;
extern msg_info msg_config_error;
extern msg_info msg_fd;
extern msg_info msg_packets;
extern msg_info msg_int_error;
extern msg_info msg_system_error;
extern msg_info msg_connections_try;
extern msg_info msg_connections_error;
extern msg_info msg_connections;
extern msg_info msg_probe_info;
extern msg_info msg_probe_error;
#endif /* LOG_H */
+513
View File
@@ -0,0 +1,513 @@
/*
# probe.c: Code for probing protocols
#
# Copyright (C) 2007-2021 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
*/
#define _GNU_SOURCE
#include <stdio.h>
#ifdef ENABLE_REGEX
#define PCRE2_CODE_UNIT_WIDTH 8
#include <pcre2.h>
#endif
#include <ctype.h>
#include "probe.h"
#include "log.h"
static int is_ssh_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item*);
static int is_openvpn_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item*);
static int is_wireguard_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item*);
static int is_tinc_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item*);
static int is_xmpp_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item*);
static int is_http_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item*);
static int is_tls_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item*);
static int is_adb_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item*);
static int is_socks5_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item*);
static int is_syslog_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item*);
static int is_teamspeak_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item*);
static int is_msrdp_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item*);
static int is_true(const char *p, ssize_t len, struct sslhcfg_protocols_item* proto) { return 1; }
/* Table of protocols that have a built-in probe
*/
static struct protocol_probe_desc builtins[] = {
/* description probe */
{ "ssh", is_ssh_protocol},
{ "openvpn", is_openvpn_protocol },
{ "wireguard", is_wireguard_protocol },
{ "tinc", is_tinc_protocol },
{ "xmpp", is_xmpp_protocol },
{ "http", is_http_protocol },
{ "tls", is_tls_protocol },
{ "adb", is_adb_protocol },
{ "socks5", is_socks5_protocol },
{ "syslog", is_syslog_protocol },
{ "teamspeak", is_teamspeak_protocol },
{ "msrdp", is_msrdp_protocol },
{ "anyprot", is_true }
};
/* TODO I think this has to go */
struct protocol_probe_desc* get_builtins(void) {
return builtins;
}
int get_num_builtins(void) {
return ARRAY_SIZE(builtins);
}
/* Returns the protocol to connect to in case of timeout;
* if not found, return the first protocol specified
*/
struct sslhcfg_protocols_item* timeout_protocol(void)
{
int i;
for (i = 0; i < cfg.protocols_len; i++) {
if (!strcmp(cfg.protocols[i].name, cfg.on_timeout)) return &cfg.protocols[i];
}
return &cfg.protocols[0];
}
/* From http://grapsus.net/blog/post/Hexadecimal-dump-in-C */
#define HEXDUMP_COLS 16
void hexdump(msg_info msg_info, const char *mem, unsigned int len)
{
unsigned int i, j;
char str[10 + HEXDUMP_COLS * 4 + 2];
int c = 0; /* index in str */
for(i = 0; i < len + ((len % HEXDUMP_COLS) ? (HEXDUMP_COLS - len % HEXDUMP_COLS) : 0); i++)
{
/* print offset */
if(i % HEXDUMP_COLS == 0)
c += sprintf(&str[c], "0x%06x: ", i);
/* print hex data */
if(i < len)
c += sprintf(&str[c], "%02x ", 0xFF & mem[i]);
else /* end of block, just aligning for ASCII dump */
c+= sprintf(&str[c], " ");
/* print ASCII dump */
if(i % HEXDUMP_COLS == (HEXDUMP_COLS - 1)) {
for(j = i - (HEXDUMP_COLS - 1); j <= i; j++) {
if(j >= len) /* end of block, not really printing */
str[c++] = ' ';
else if(isprint(mem[j])) /* printable char */
str[c++] = 0xFF & mem[j];
else /* other char */
str[c++] = '.';
}
str[c++] = '\n';
str[c++] = 0;
print_message(msg_info, "%s", str);
c = 0;
}
}
}
/* Is the buffer the beginning of an SSH connection? */
static int is_ssh_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item* proto)
{
if (len < 4)
return PROBE_AGAIN;
return !strncmp(p, "SSH-", 4);
}
/* Is the buffer the beginning of an OpenVPN connection?
*
* Code inspired from OpenVPN port-share option; however, OpenVPN code is
* wrong: users using pre-shared secrets have non-initialised key_id fields so
* p[3] & 7 should not be looked at, and also the key_method can be specified
* to 1 which changes the opcode to P_CONTROL_HARD_RESET_CLIENT_V1.
* See:
* http://www.fengnet.com/book/vpns%20illustrated%20tunnels%20%20vpnsand%20ipsec/ch08lev1sec5.html
* and OpenVPN ssl.c, ssl.h and options.c
*/
#define OVPN_OPCODE_MASK 0xF8
#define OVPN_CONTROL_HARD_RESET_CLIENT_V1 (0x01 << 3)
#define OVPN_CONTROL_HARD_RESET_CLIENT_V2 (0x07 << 3)
#define OVPN_HMAC_128 16
#define OVPN_HMAC_160 20
#define OVPN_HARD_RESET_PACKET_ID_OFFSET(hmac_size) (9 + hmac_size)
static int is_openvpn_protocol (const char*p,ssize_t len, struct sslhcfg_protocols_item* proto)
{
int packet_len;
if (proto->is_udp == 0)
{
if (len < 2)
return PROBE_AGAIN;
packet_len = ntohs(*(uint16_t*)p);
return packet_len == len - 2;
} else {
if (len < 1)
return PROBE_NEXT;
if ((p[0] & OVPN_OPCODE_MASK) != OVPN_CONTROL_HARD_RESET_CLIENT_V1 &&
(p[0] & OVPN_OPCODE_MASK) != OVPN_CONTROL_HARD_RESET_CLIENT_V2)
return PROBE_NEXT;
/* The detection pattern above may not be reliable enough.
* Check the packet id: OpenVPN sents five initial packets
* whereas the packet id is increased with every transmitted datagram.
*/
if (len <= OVPN_HARD_RESET_PACKET_ID_OFFSET(OVPN_HMAC_128) + sizeof(uint32_t))
return PROBE_NEXT;
if (ntohl(*(uint32_t*)(p + OVPN_HARD_RESET_PACKET_ID_OFFSET(OVPN_HMAC_128))) <= 5u)
return PROBE_MATCH;
if (len <= OVPN_HARD_RESET_PACKET_ID_OFFSET(OVPN_HMAC_160) + sizeof(uint32_t))
return PROBE_NEXT;
if (ntohl(*(uint32_t*)(p + OVPN_HARD_RESET_PACKET_ID_OFFSET(OVPN_HMAC_160))) <= 5u)
return PROBE_MATCH;
return PROBE_NEXT;
}
}
static int is_wireguard_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item* proto)
{
if (proto->is_udp == 0)
return PROBE_NEXT;
// Handshake Init: 148 bytes
if (len != 148)
return PROBE_NEXT;
// Handshake Init: p[0] = 0x01, p[1..3] = 0x000000 (reserved)
if (ntohl(*(uint32_t*)p) != 0x01000000)
return PROBE_NEXT;
return PROBE_MATCH;
}
/* Is the buffer the beginning of a tinc connections?
* Protocol is documented here: http://www.tinc-vpn.org/documentation/tinc.pdf
* First connection starts with "0 " in 1.0.15)
* */
static int is_tinc_protocol( const char *p, ssize_t len, struct sslhcfg_protocols_item* proto)
{
if (len < 2)
return PROBE_AGAIN;
return !strncmp(p, "0 ", 2);
}
/* Is the buffer the beginning of a jabber (XMPP) connections?
* (Protocol is documented (http://tools.ietf.org/html/rfc6120) but for lazy
* clients, just checking first frame containing "jabber" in xml entity)
* */
static int is_xmpp_protocol( const char *p, ssize_t len, struct sslhcfg_protocols_item* proto)
{
if (memmem(p, len, "jabber", 6))
return PROBE_MATCH;
/* sometimes the word 'jabber' shows up late in the initial string,
sometimes after a newline. this makes sure we snarf the entire preamble
and detect it. (fixed for adium/pidgin) */
if (len < 50)
return PROBE_AGAIN;
return PROBE_NEXT;
}
static int probe_http_method(const char *p, int len, const char *opt)
{
if (len < strlen(opt))
return PROBE_AGAIN;
return !strncmp(p, opt, strlen(opt));
}
/* Is the buffer the beginning of an HTTP connection? */
static int is_http_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item* proto)
{
int res;
/* If it's got HTTP in the request (HTTP/1.1) then it's HTTP */
if (memmem(p, len, "HTTP", 4))
return PROBE_MATCH;
#define PROBE_HTTP_METHOD(opt) if ((res = probe_http_method(p, len, opt)) != PROBE_NEXT) return res
/* Otherwise it could be HTTP/1.0 without version: check if it's got an
* HTTP method (RFC2616 5.1.1) */
PROBE_HTTP_METHOD("OPTIONS");
PROBE_HTTP_METHOD("GET");
PROBE_HTTP_METHOD("HEAD");
PROBE_HTTP_METHOD("POST");
PROBE_HTTP_METHOD("PUT");
PROBE_HTTP_METHOD("DELETE");
PROBE_HTTP_METHOD("TRACE");
PROBE_HTTP_METHOD("CONNECT");
#undef PROBE_HTTP_METHOD
return PROBE_NEXT;
}
/* Says if it's TLS, optionally with SNI and ALPN lists in proto->data */
static int is_tls_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item* proto)
{
switch (parse_tls_header(proto->data, p, len)) {
case TLS_MATCH: return PROBE_MATCH;
case TLS_NOMATCH: return PROBE_NEXT;
case TLS_ELENGTH: return PROBE_AGAIN;
default: return PROBE_NEXT;
}
}
static int probe_adb_cnxn_message(const char *p)
{
/* The initial ADB host->device packet has a command type of CNXN, and a
* data payload starting with "host:". Note that current versions of the
* client hardcode "host::" (with empty serialno and banner fields) but
* other clients may populate those fields.
*/
return !memcmp(&p[0], "CNXN", 4) && !memcmp(&p[24], "host:", 5);
}
static int is_adb_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item* proto)
{
/* amessage.data_length is not being checked, under the assumption that
* a packet >= 30 bytes will have "something" in the payload field.
*
* 24 bytes for the message header and 5 bytes for the "host:" tag.
*
* ADB protocol:
* https://android.googlesource.com/platform/system/adb/+/master/protocol.txt
*/
static const unsigned int min_data_packet_size = 30;
if (len < min_data_packet_size)
return PROBE_AGAIN;
if (probe_adb_cnxn_message(&p[0]) == PROBE_MATCH)
return PROBE_MATCH;
/* In ADB v26.0.0 rc1-4321094, the initial host->device packet sends an
* empty message before sending the CNXN command type. This was an
* unintended side effect introduced in
* https://android-review.googlesource.com/c/342653, and will be reverted for
* a future release.
*/
static const unsigned char empty_message[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff
};
if (len < min_data_packet_size + sizeof(empty_message))
return PROBE_AGAIN;
if (memcmp(&p[0], empty_message, sizeof(empty_message)))
return PROBE_NEXT;
return probe_adb_cnxn_message(&p[sizeof(empty_message)]);
}
static int is_socks5_protocol(const char *p_in, ssize_t len, struct sslhcfg_protocols_item* proto)
{
unsigned char* p = (unsigned char*)p_in;
int i;
if (len < 2)
return PROBE_AGAIN;
/* First byte should be socks protocol version */
if (p[0] != 5)
return PROBE_NEXT;
/* Second byte should be number of supported
* authentication methods, assuming maximum of 10,
* as defined in https://www.iana.org/assignments/socks-methods/socks-methods.xhtml
*/
char m_count = p[1];
if (m_count < 1 || m_count > 10)
return PROBE_NEXT;
if (len < 2 + m_count)
return PROBE_AGAIN;
/* Each authentication method number should be in range 0..9
* (https://www.iana.org/assignments/socks-methods/socks-methods.xhtml)
*/
for (i = 0; i < m_count; i++) {
if (p[2 + i] > 9)
return PROBE_NEXT;
}
return PROBE_MATCH;
}
static int is_syslog_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item* proto)
{
int res, i, j;
res = sscanf(p, "<%d>", &i);
if (res == 1) return 1;
res = sscanf(p, "%d <%d>", &i, &j);
if (res == 2) return 1;
return 0;
}
static int is_teamspeak_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item* proto)
{
if (len < 8)
return PROBE_NEXT;
return !strncmp(p, "TS3INIT1", len);
}
static int is_msrdp_protocol(const char *p, ssize_t len, struct sslhcfg_protocols_item* proto)
{
char version;
char packet_len;
if (len < 7)
return PROBE_NEXT;
version=*p;
if (version!=0x03)
return 0;
packet_len = ntohs(*(uint16_t*)(p+2));
return packet_len == len;
}
static int regex_probe(const char *p, ssize_t len, struct sslhcfg_protocols_item* proto)
{
#ifdef ENABLE_REGEX
pcre2_code**probe = (pcre2_code**)proto->data;
pcre2_match_data* matches;
matches = pcre2_match_data_create(1, NULL);
for (; *probe; probe++) {
int res = pcre2_match(*probe, (PCRE2_SPTR8)p, len, 0, 0, matches, NULL);
if (res >= 0) return 1;
}
return 0;
#else
/* Should never happen as we check when loading config file */
print_message(msg_int_error, "FATAL: regex probe called but not built in\n");
exit(5);
#endif
}
/* Run all the probes on a buffer
* buf, len: buffer to test on
* proto_in, proto_len: array of protocols to try
* proto_out: protocol that matched
*
* Returns
* PROBE_AGAIN if not enough data, and set *proto to NULL
* PROBE_MATCH if protocol is identified, in which case *proto is set to
* point to the appropriate protocol
* */
int probe_buffer(char* buf, int len,
struct sslhcfg_protocols_item** proto_in,
int proto_len,
struct sslhcfg_protocols_item** proto_out
)
{
struct sslhcfg_protocols_item* p;
int i, res, again = 0;
print_message(msg_packets, "hexdump of incoming packet:\n");
hexdump(msg_packets, buf, len);
*proto_out = NULL;
for (i = 0; i < proto_len; i++) {
char* probe_str[3] = {"PROBE_NEXT", "PROBE_MATCH", "PROBE_AGAIN"};
p = proto_in[i];
if (! p->probe) continue;
print_message(msg_probe_info, "probing for %s\n", p->name);
/* Don't probe last protocol if it is anyprot (and store last protocol) */
if ((i == proto_len - 1) && (!strcmp(p->name, "anyprot")))
break;
if (p->minlength_is_present && (len < p->minlength )) {
print_message(msg_probe_info, "input too short, %d bytes but need %d\n",
len , p->minlength);
again++;
continue;
}
res = p->probe(buf, len, p);
print_message(msg_probe_info, "probed for %s: %s\n", p->name, probe_str[res]);
if (res == PROBE_MATCH) {
*proto_out = p;
return PROBE_MATCH;
}
if (res == PROBE_AGAIN)
again++;
}
if (again)
return PROBE_AGAIN;
/* Everything failed: match the last one */
if (proto_len == 0) {
/* This should be caught by configuration sanity checks, but just in
* case, die gracefully rather than segfaulting */
print_message(msg_int_error, "Received traffic on transport that has no target\n");
exit(0);
}
*proto_out = proto_in[proto_len-1];
return PROBE_MATCH;
}
/* Returns the probe for specified protocol:
* parameter is the description in builtins[], or "regex"
* */
T_PROBE* get_probe(const char* description) {
int i;
for (i = 0; i < ARRAY_SIZE(builtins); i++) {
if (!strcmp(builtins[i].name, description)) {
return builtins[i].probe;
}
}
/* Special case of "regex" probe (we don't want to set it in builtins
* because builtins is also used to build the command-line options and
* regexp is not legal on the command line)*/
if (!strcmp(description, "regex"))
return regex_probe;
/* Special case of "timeout" is allowed as a probe name in the
* configuration file even though it's not really a probe */
if (!strcmp(description, "timeout"))
return is_true;
return NULL;
}
+69
View File
@@ -0,0 +1,69 @@
/* API for probe.c */
#ifndef PROBE_H
#define PROBE_H
#include "common.h"
#include "tls.h"
#include "log.h"
typedef enum {
PROBE_NEXT, /* Enough data, probe failed -- it's some other protocol */
PROBE_MATCH, /* Enough data, probe successful -- it's the current protocol */
PROBE_AGAIN, /* Not enough data for this probe, try again with more data */
} probe_result;
struct sslhcfg_protocols_item;
typedef int T_PROBE(const char*, ssize_t, struct sslhcfg_protocols_item*);
struct protocol_probe_desc {
const char* name;
T_PROBE* probe;
};
#include "sslh-conf.h"
/* Returns a pointer to the array of builtin protocols */
struct protocol_probe_desc* get_builtins(void);
/* Returns the number of builtin protocols */
int get_num_builtins(void);
/* Returns the probe for specified protocol */
T_PROBE* get_probe(const char* description);
/* Returns the head of the configured protocols */
struct sslhcfg_protocols_item* get_first_protocol(void);
/* Set the list of configured protocols */
void set_protocol_list(struct sslhcfg_protocols_item*);
/* probe_client_protocol
*
* Read the beginning of data coming from the client connection and check if
* it's a known protocol. Then leave the data on the deferred
* write buffer of the connection and returns a pointer to the protocol
* structure
*/
int probe_client_protocol(struct connection *cnx);
/* Probe on a buffer */
int probe_buffer(char* buf, int len,
struct sslhcfg_protocols_item** proto_in,
int proto_len,
struct sslhcfg_protocols_item** proto_out
);
/* set the protocol to connect to in case of timeout */
void set_ontimeout(const char* name);
/* timeout_protocol
*
* Returns the protocol to connect to in case of timeout
*/
struct sslhcfg_protocols_item* timeout_protocol(void);
void hexdump(msg_info, const char*, unsigned int);
#endif
+115
View File
@@ -0,0 +1,115 @@
/*
Processes that are common to sslh-ev and sslh-select
# Copyright (C) 2021 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 "udp-listener.h"
#include "tcp-listener.h"
#include "processes.h"
#include "probe.h"
#include "log.h"
int tidy_connection(struct connection *cnx, struct loop_info* fd_info)
{
int i;
for (i = 0; i < 2; i++) {
if (cnx->q[i].fd != -1) {
print_message(msg_fd, "closing fd %d\n", cnx->q[i].fd);
watchers_del_read(fd_info->watchers, cnx->q[i].fd);
watchers_del_write(fd_info->watchers, cnx->q[i].fd);
close(cnx->q[i].fd);
if (cnx->q[i].deferred_data)
free(cnx->q[i].deferred_data);
}
}
if (cnx->type == SOCK_DGRAM)
udp_tidy(cnx, fd_info);
if (gap_remove_ptr(fd_info->probing_list, cnx, fd_info->num_probing) != -1)
fd_info->num_probing--;
collection_remove_cnx(fd_info->collection, cnx);
return 0;
}
/* Process a connection that is active in read */
void cnx_read_process(struct loop_info* fd_info, int fd)
{
cnx_collection* collection = fd_info->collection;
struct connection* cnx = collection_get_cnx_from_fd(collection, fd);
switch (cnx->type) {
case SOCK_STREAM:
tcp_read_process(fd_info, fd);
break;
case SOCK_DGRAM:
udp_s2c_forward(cnx);
break;
default:
print_message(msg_int_error, "cnx_read_process: Illegal connection type %d\n", cnx->type);
dump_connection(cnx);
exit(1);
}
}
/* Process a connection that accepts a socket
* (For UDP, this means all traffic coming from remote clients)
* Returns new connection object, or NULL
* */
struct connection* cnx_accept_process(struct loop_info* fd_info, struct listen_endpoint* listen_socket)
{
int fd = listen_socket->socketfd;
int type = listen_socket->type;
struct connection* cnx;
switch (type) {
case SOCK_STREAM:
cnx = accept_new_connection(fd, fd_info);
if (!cnx) return NULL;
break;
case SOCK_DGRAM:
cnx = udp_c2s_forward(fd, fd_info);
if (!cnx) return NULL;
break;
default:
print_message(msg_int_error, "Inconsistent cnx type: %d\n", type);
exit(1);
}
int new_fd = cnx->q[0].fd;
watchers_add_read(fd_info->watchers, new_fd);
return cnx;
}
+41
View File
@@ -0,0 +1,41 @@
#ifndef PROCESSES_H
#define PROCESSES_H
#include "common.h"
#include "collection.h"
#include "gap.h"
typedef struct connection* hash_item;
#include "hash.h"
/* Provided by event loop, sslh-ev or sslh-select, for implementation-dependant
* data */
typedef struct watchers watchers;
/* Global state for a loop */
struct loop_info {
int num_probing; /* Number of connections currently probing
* We use this to know if we need to time out of
* select() */
gap_array* probing_list; /* Pointers to cnx that are in probing mode */
hash* hash_sources; /* UDP remote sources previously encountered */
watchers* watchers;
cnx_collection* collection; /* Collection of connections linked to this loop */
};
void cnx_read_process(struct loop_info* fd_info, int fd);
struct connection* cnx_accept_process(struct loop_info* fd_info, struct listen_endpoint* listen_socket);
int tidy_connection(struct connection *cnx, struct loop_info* fd_info);
/* These must be declared in the loop handler, sslh-ev or sslh-select */
void watchers_add_read(watchers* w, int fd);
void watchers_del_read(watchers* w, int fd);
void watchers_add_write(watchers* w, int fd);
void watchers_del_write(watchers* w, int fd);
#endif
-4
View File
@@ -1,4 +0,0 @@
PIDFILE=/var/run/sslh.pid
LISTEN=ifname:443
SSH=localhost:22
SSL=localhost:443
+7 -10
View File
@@ -2,6 +2,8 @@
### BEGIN INIT INFO
# Provides: sslh
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 1
# Short-Description: sslh proxy ssl & ssh connections
@@ -13,23 +15,19 @@ facility=user.info
# /etc/init.d/sslh: start and stop the sslh proxy daemon
# Defaults -- can be overridden in /etc/default/sslh
PIDFILE=/var/run/sslh.pid
LISTEN=thelonious:443
SSH=localhost:22
SSL=localhost:443
if test -f /etc/default/sslh; then
. /etc/default/sslh
export PIDFILE=${PIDFILE}
fi
DAEMON=/usr/local/sbin/sslh
# The prefix is normally filled by make install. If
# installing by hand, fill it in yourself!
PREFIX=
DAEMON=$PREFIX/sbin/sslh
start()
{
echo "Start services: sslh"
$DAEMON -u nobody -p ${LISTEN} -s ${SSH} -l ${SSL}
$DAEMON -F/etc/sslh.cfg
logger -t ${tag} -p ${facility} -i 'Started sslh'
}
@@ -37,7 +35,6 @@ stop()
{
echo "Stop services: sslh"
killall $DAEMON
rm ${PIDFILE}
logger -t ${tag} -p ${facility} -i 'Stopped sslh'
}
+75
View File
@@ -0,0 +1,75 @@
#!/bin/bash
#
# sslh Startup script for the SSL/SSH multiplexer
#
# chkconfig: - 13 87
# description: Sslh accepts connections on specified ports, and forwards
# them further based on tests performed on the first data
# packet sent by the remote client.
# processname: sslh
# config: /etc/sslh.cfg
# config: /etc/sysconfig/sslh
# pidfile: /var/run/sslh/sslh.pid
#
# Authors:
# Andre Krajnik akrajnik@gmail.com - 2010-03-20
# Julien Thomas julthomas@free.fr - 2013-08-25
# Source function library.
. /etc/init.d/functions
if [ -f /etc/sysconfig/sslh ]; then
. /etc/sysconfig/sslh
fi
PROGNAME=sslh
SSLH=${SSLH:-/usr/sbin/sslh-select}
SSLH_LANG=${SSLH_LANG:-C}
CONFIG=${CONFIG:-/etc/sslh.cfg}
PIDFILE=${PIDFILE:-/var/run/sslh/sslh.pid}
LOCKFILE=${LOCKFILE:-/var/lock/subsys/sslh}
STOP_TIMEOUT=${STOP_TIMEOUT:-10}
RETVAL=0
start() {
echo -n "Starting $PROGNAME: "
LANG=$SSLH_LANG daemon --pidfile="$PIDFILE" \
${SSLH_USER:+--user="${SSLH_USER}"} \
"$SSLH" ${CONFIG:+-F "$CONFIG"} "$OPTIONS"
RETVAL=$?
echo
[ $RETVAL = 0 ] && touch "$LOCKFILE"
return $RETVAL
}
stop() {
echo -n "Stopping $PROGNAME: "
killproc -p "$PIDFILE" -d "$STOP_TIMEOUT" "$SSLH"
RETVAL=$?
echo
[ $RETVAL = 0 ] && rm -f "$LOCKFILE" "$PIDFILE"
}
# See how we were called.
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status -p "$PIDFILE" "$SSLH"
RETVAL=$?
;;
restart)
stop
start
;;
*)
echo "Usage: $PROGNAME {start|stop|status|restart}"
RETVAL=2
;;
esac
exit $RETVAL
+36
View File
@@ -0,0 +1,36 @@
#
# The default processing model uses select
# A fork model is also available
#
#SSLH=/usr/sbin/sslh-select
#
# If transparent mode is enabled, the following
# is needed in order to run as sslh user
#
#SSLH_USER=sslh
#setcap cap_net_bind_service,cap_net_raw=+ep $SSLH
#
# Configuration file for sslh
# Set empty to disable configuration file support
#
#CONFIG=/etc/sslh.cfg
#
# Extra option to pass on command line
# Those can supersede configuration file settings
#
#OPTIONS=
#
# The sslh process is started by default with the C
# locale, it can be changed here
#
#SSLH_LANG=C
#
# If an alternate location is specified in configuration
# file, it needs to be reported here
#
#PIDFILE=/var/run/sslh/sslh.pid
+9
View File
@@ -0,0 +1,9 @@
# Add the following to your fail2ban jail.conf
# In Debian you'd append it to /etc/fail2ban/jail.local
[sslh-ssh]
enabled = true
filter = sslh-ssh
action = iptables-multiport[name=sslh,port="443"]
logpath = /var/log/messages
maxretry = 5
+25
View File
@@ -0,0 +1,25 @@
# Add the following to you fail2ban configuration file
# In Debian it'd go in /etc/fail2ban/filter.d/sslh-ssh.conf
# Fail2Ban filter for sslh demultiplexed ssh
#
# Doesn't (and cannot) detect auth errors,
# but many connection attempts from the same
# origin is reason enough to block.
#
# Version: 2014-03-28
[INCLUDES]
# no includes
[Definition]
failregex = ^.+ sslh\[.+\]: ssh:connection from <HOST>:.+ to .+ forwarded
from .+ to .+:ssh\s*$
ignoreregex =
# Author: Evert Mouw <post@evert.net>
+27
View File
@@ -0,0 +1,27 @@
[Unit]
Description=SSL/SSH multiplexer
After=network.target
[Service]
EnvironmentFile=/etc/conf.d/sslh
ExecStart=/usr/sbin/sslh --foreground $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
+2454
View File
File diff suppressed because it is too large. Load diff
+129
View File
@@ -0,0 +1,129 @@
/* Generated by conf2struct (https://www.rutschle.net/tech/conf2struct/README)
* on Sun Sep 11 21:43:25 2022.
# conf2struct: generate libconf parsers that read to structs
# Copyright (C) 2018-2021 Yves Rutschle
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef C2S_SSLHCFG_H
#define C2S_SSLHCFG_H
#ifdef LIBCONFIG
# include <libconfig.h>
#endif
#include "probe.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
struct sslhcfg_listen_item {
char* host;
char* port;
int is_udp;
int keepalive;
};
struct sslhcfg_protocols_item {
char* name;
char* host;
char* port;
int service_is_present;
char* service;
int is_udp;
int udp_timeout;
int fork;
int tfo_ok;
int transparent;
int resolve_on_forward;
int log_level;
int keepalive;
size_t sni_hostnames_len;
char** sni_hostnames;
size_t alpn_protocols_len;
char** alpn_protocols;
size_t regex_patterns_len;
char** regex_patterns;
int minlength_is_present;
int minlength;
T_PROBE* probe;
struct addrinfo* saddr;
void* data;
dl_list timeouts;
};
struct sslhcfg_item {
int verbose_config;
int verbose_config_error;
int verbose_connections;
int verbose_connections_try;
int verbose_connections_error;
int verbose_fd;
int verbose_packets;
int verbose_probe_info;
int verbose_probe_error;
int verbose_system_error;
int verbose_int_error;
int version;
int foreground;
int inetd;
int numeric;
int transparent;
int timeout;
int udp_max_connections;
int user_is_present;
char* user;
int pidfile_is_present;
char* pidfile;
int chroot_is_present;
char* chroot;
char* syslog_facility;
int logfile_is_present;
char* logfile;
char* on_timeout;
char* prefix;
size_t listen_len;
struct sslhcfg_listen_item* listen;
size_t protocols_len;
struct sslhcfg_protocols_item* protocols;
};
int sslhcfg_parse_file(
const char* filename,
struct sslhcfg_item* sslhcfg,
const char** errmsg);
void sslhcfg_fprint(
FILE* out,
struct sslhcfg_item *sslhcfg,
int depth);
int sslhcfg_cl_parse(
int argc,
char* argv[],
struct sslhcfg_item *sslhcfg);
#endif
+153
View File
@@ -0,0 +1,153 @@
/*
sslh-ev: mono-processus server based on libev
# Copyright (C) 2021 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 <stdlib.h>
#include <ev.h>
#include "gap.h"
#include "log.h"
#include "udp-listener.h"
#include "tcp-listener.h"
const char* server_type = "sslh-ev";
static struct ev_loop* loop;
/* Libev watchers */
struct watchers {
/* one set of ev_io for read, one for write, indexed by file descriptor */
gap_array *ev_ior, *ev_iow;
struct listen_endpoint* listen_sockets;
gap_array* fd2ls; /* Array indexed by file descriptor, pointing to listen_sockets */
};
static void cnx_read_cb(EV_P_ ev_io *w, int revents);
static void cnx_write_cb(EV_P_ ev_io *w, int wevents);
static void cnx_accept_cb(EV_P_ ev_io *w, int revents);
static void watchers_init(watchers** w, struct listen_endpoint* listen_sockets,
int num_addr_listen)
{
*w = malloc(sizeof(**w));
(*w)->ev_ior = gap_init(num_addr_listen);
(*w)->ev_iow = gap_init(num_addr_listen);
(*w)->listen_sockets = listen_sockets;
(*w)->fd2ls = gap_init(0);
/* Create watchers for listen sockets */
for (int i = 0; i < num_addr_listen; i++) {
ev_io* io = malloc(sizeof(*io));
ev_io_init(io, &cnx_accept_cb, listen_sockets[i].socketfd, EV_READ);
ev_io_start(EV_A_ io);
gap_set((*w)->ev_ior, i, io);
gap_set((*w)->fd2ls, listen_sockets[i].socketfd, &listen_sockets[i]);
set_nonblock(listen_sockets[i].socketfd);
}
}
void watchers_add_read(watchers* w, int fd)
{
ev_io* io = gap_get(w->ev_ior, fd);
if (!io) {
io = malloc(sizeof(*io));
ev_io_init(io, &cnx_read_cb, fd, EV_READ);
ev_io_set(io, fd, EV_READ);
gap_set(w->ev_ior, fd, io);
}
ev_io_start(loop, io);
}
void watchers_del_read(watchers* w, int fd)
{
ev_io* io = gap_get(w->ev_ior, fd);
if (io) ev_io_stop(EV_A_ io);
}
void watchers_add_write(watchers* w, int fd)
{
ev_io* io = gap_get(w->ev_iow, fd);
if (!io) {
io = malloc(sizeof(*io));
ev_io_init(io, &cnx_write_cb, fd, EV_WRITE);
ev_io_set(io, fd, EV_WRITE);
gap_set(w->ev_iow, fd, io);
}
ev_io_start(loop, io);
}
void watchers_del_write(watchers* w, int fd)
{
ev_io* io = gap_get(w->ev_iow, fd);
if (io) ev_io_stop(EV_A_ io);
}
/* /watchers */
#include "processes.h"
/* Libev callbacks */
static void cnx_read_cb(EV_P_ ev_io *w, int revents)
{
struct loop_info* info = ev_userdata(EV_A);
cnx_read_process(info, w->fd);
}
static void cnx_write_cb(EV_P_ ev_io *w, int wevents)
{
struct loop_info* info = ev_userdata(EV_A);
cnx_write_process(info, w->fd);
}
static void cnx_accept_cb(EV_P_ ev_io *w, int revents)
{
struct loop_info* info = ev_userdata(EV_A);
cnx_accept_process(info, gap_get(info->watchers->fd2ls, w->fd));
}
void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen)
{
struct loop_info ev_info = {0};
loop = EV_DEFAULT;
ev_info.collection = collection_init(0);
ev_info.probing_list = gap_init(0);
udp_init(&ev_info);
tcp_init();
watchers_init(&ev_info.watchers, listen_sockets, num_addr_listen);
ev_set_userdata(EV_A_ &ev_info);
ev_run(EV_A_ 0);
}
void start_shoveler(int listen_socket) {
print_message(msg_config_error, "inetd mode is not supported in libev mode\n");
exit(1);
}
+250
View File
@@ -0,0 +1,250 @@
/*
sslh-fork: forking server
# Copyright (C) 2007-2021 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 "common.h"
#include "probe.h"
#include "sslh-conf.h"
#include "tcp-probe.h"
#include "log.h"
#ifdef LIBBSD
#include <bsd/unistd.h>
#endif
const char* server_type = "sslh-fork";
/* shovels data from one fd to the other and vice-versa
returns after one socket closed
*/
int shovel(struct connection *cnx)
{
fd_set fds;
int res, i;
int max_fd = MAX(cnx->q[0].fd, cnx->q[1].fd) + 1;
FD_ZERO(&fds);
while (1) {
FD_SET(cnx->q[0].fd, &fds);
FD_SET(cnx->q[1].fd, &fds);
res = select(
max_fd,
&fds,
NULL,
NULL,
NULL
);
CHECK_RES_DIE(res, "select");
for (i = 0; i < 2; i++) {
if (FD_ISSET(cnx->q[i].fd, &fds)) {
res = fd2fd(&cnx->q[1-i], &cnx->q[i]);
if (res == FD_CNXCLOSED) {
print_message(msg_fd, "%s %s", i ? "client" : "server", "socket closed\n");
return res;
}
}
}
}
}
/* Child process that finds out what to connect to and proxies
*/
void start_shoveler(int in_socket)
{
fd_set fds;
struct timeval tv;
int res = PROBE_AGAIN;
int out_socket;
struct connection cnx;
struct connection_desc desc;
init_cnx(&cnx);
cnx.q[0].fd = in_socket;
FD_ZERO(&fds);
FD_SET(in_socket, &fds);
memset(&tv, 0, sizeof(tv));
tv.tv_sec = cfg.timeout;
while (res == PROBE_AGAIN) {
/* POSIX does not guarantee that tv will be updated, but the client can
* only postpone the inevitable for so long */
res = select(in_socket + 1, &fds, NULL, NULL, &tv);
if (res == -1)
perror("select");
if (FD_ISSET(in_socket, &fds)) {
/* Received data: figure out what protocol it is */
res = probe_client_protocol(&cnx);
} else {
/* Timed out: it's necessarily SSH */
cnx.proto = timeout_protocol();
print_message(msg_fd, "timed out, connect to %s\n", cnx.proto->name);
break;
}
}
if (cnx.proto->service &&
check_access_rights(in_socket, cnx.proto->service)) {
exit(0);
}
/* Connect the target socket */
out_socket = connect_addr(&cnx, in_socket, BLOCKING);
CHECK_RES_DIE(out_socket, "connect");
set_capabilities(0);
cnx.q[1].fd = out_socket;
get_connection_desc(&desc, &cnx);
log_connection(&desc, &cnx);
set_proctitle_shovel(&desc, &cnx);
flush_deferred(&cnx.q[1]);
shovel(&cnx);
close(in_socket);
close(out_socket);
print_message(msg_fd, "connection closed down\n");
exit(0);
}
static pid_t *listener_pid;
static int listener_pid_number = 0;
void stop_listeners(int sig)
{
int i;
for (i = 0; i < listener_pid_number; i++) {
kill(listener_pid[i], sig);
}
}
void set_listen_procname(struct listen_endpoint *listen_socket)
{
#ifdef LIBBSD
int res;
struct addrinfo addr;
struct sockaddr_storage ss;
char listen_addr[NI_MAXHOST+1+NI_MAXSERV+1];
addr.ai_addr = (struct sockaddr*)&ss;
addr.ai_addrlen = sizeof(ss);
res = getsockname(listen_socket->socketfd, addr.ai_addr, &addr.ai_addrlen);
if (res != -1) {
sprintaddr(listen_addr, sizeof(listen_addr), &addr);
setproctitle("listener %s", listen_addr);
}
#endif
}
/* TCP listener: connections, fork a child for each new connection
* IN:
* endpoint: array of listening endpoint objects
* num_endpoints: size of endpoint array
* active_endpoint: which endpoint is this listener working on
* Does not return
* */
void tcp_listener(struct listen_endpoint* endpoint, int num_endpoints, int active_endpoint)
{
int i, in_socket;
while (1) {
in_socket = accept(endpoint[active_endpoint].socketfd, 0, 0);
CHECK_RES_RETURN(in_socket, "accept", /*void*/ );
print_message(msg_fd, "accepted fd %d\n", in_socket);
switch(fork()) {
case -1: print_message(msg_system_error, "fork failed: err %d: %s\n", errno, strerror(errno));
break;
case 0: /* In child process */
/* Shoveler processes don't need to hog file descriptors */
for (i = 0; i < num_endpoints; i++)
close(endpoint[i].socketfd);
start_shoveler(in_socket);
exit(0);
default: /* In parent process */
break;
}
close(in_socket);
}
}
void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen)
{
int i, res;
struct sigaction action;
listener_pid_number = num_addr_listen;
listener_pid = malloc(listener_pid_number * sizeof(listener_pid[0]));
CHECK_ALLOC(listener_pid, "malloc");
tcp_init();
/* Start one process for each listening address */
for (i = 0; i < num_addr_listen; i++) {
listener_pid[i] = fork();
switch(listener_pid[i]) {
/* Log if fork() fails for some reason */
case -1: print_message(msg_system_error, "fork failed: err %d: %s\n", errno, strerror(errno));
break;
/* We're in the child, we have work to do */
case 0:
set_listen_procname(&listen_sockets[i]);
if (listen_sockets[i].type == SOCK_DGRAM)
print_message(msg_config_error, "UDP not (yet?) supported in sslh-fork\n");
else
tcp_listener(listen_sockets, num_addr_listen, i);
break;
/* We're in the parent, we don't need to do anything */
default:
break;
}
}
/* Set SIGTERM to "stop_listeners" which further kills all listener
* processes. Note this won't kill processes that listeners forked, which
* means active connections remain active. */
memset(&action, 0, sizeof(action));
action.sa_handler = stop_listeners;
res = sigaction(SIGTERM, &action, NULL);
CHECK_RES_DIE(res, "sigaction");
wait(NULL);
}
/* The actual main is in common.c: it's the same for both version of
* the server
*/
+298
View File
@@ -0,0 +1,298 @@
/*
# main: processing of config file, command line options and start the main
# loop.
#
# Copyright (C) 2007-2018 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
*/
#define _GNU_SOURCE
#ifdef LIBCONFIG
#include <libconfig.h>
#endif
#ifdef ENABLE_REGEX
#define PCRE2_CODE_UNIT_WIDTH 8
#include <pcre2.h>
#endif
#ifdef LIBBSD
#include <bsd/unistd.h>
#endif
#include "common.h"
#include "probe.h"
#include "log.h"
/* Constants for options that have no one-character shorthand */
#define OPT_ONTIMEOUT 257
static void printcaps(void) {
#ifdef LIBCAP
cap_t caps;
char* desc;
ssize_t len;
caps = cap_get_proc();
desc = cap_to_text(caps, &len);
print_message(msg_config, "capabilities: %s\n", desc);
cap_free(caps);
cap_free(desc);
#endif
}
static void printsettings(void)
{
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 [%s] [%s] [%s]\n",
p->name,
buf,
p->service,
p->log_level,
p->keepalive ? "keepalive" : "",
p->fork ? "fork" : "",
p->transparent ? "transparent" : ""
);
}
print_message(msg_config,
"timeout: %d\n"
"on-timeout: %s\n"
"UDP hash size: %d\n",
cfg.timeout,
timeout_protocol()->name,
cfg.udp_max_connections);
}
static void setup_regex_probe(struct sslhcfg_protocols_item *p)
#ifdef ENABLE_REGEX
{
int num_patterns, i, error;
pcre2_code** pattern_list;
PCRE2_SIZE error_offset;
PCRE2_UCHAR8 err_str[120];
num_patterns = p->regex_patterns_len;
pattern_list = calloc(num_patterns + 1, sizeof(*pattern_list));
CHECK_ALLOC(pattern_list, "calloc");
p->data = (void*)pattern_list;
for (i = 0; i < num_patterns; i++) {
pattern_list[i] = pcre2_compile((PCRE2_SPTR8)p->regex_patterns[i],
PCRE2_ZERO_TERMINATED, 0,
&error, &error_offset, NULL);
if (!pattern_list[i]) {
pcre2_get_error_message(error, err_str, sizeof(err_str));
print_message(msg_config_error, "compiling pattern /%s/:%d:%s at offset %ld\n",
p->regex_patterns[i], error, err_str, error_offset);
exit(1);
}
}
}
#else
{
return;
}
#endif
/* For each protocol in the configuration, resolve address and set up protocol
* options if required
*/
static void config_protocols()
{
int i;
for (i = 0; i < cfg.protocols_len; i++) {
struct sslhcfg_protocols_item* p = &(cfg.protocols[i]);
if (
!p->resolve_on_forward &&
resolve_split_name(&(p->saddr), p->host, p->port)
) {
print_message(msg_config_error, "cannot resolve %s:%s\n",
p->host, p->port);
exit(4);
}
p->probe = get_probe(p->name);
if (!p->probe) {
print_message(msg_config_error, "%s: probe unknown\n", p->name);
exit(1);
}
if (!strcmp(cfg.protocols[i].name, "regex")) {
setup_regex_probe(&cfg.protocols[i]);
}
if (!strcmp(cfg.protocols[i].name, "tls")) {
cfg.protocols[i].data = (void*)new_tls_data();
if (cfg.protocols[i].sni_hostnames_len)
tls_data_set_list(cfg.protocols[i].data, 0,
(const char**) cfg.protocols[i].sni_hostnames,
cfg.protocols[i].sni_hostnames_len);
if (cfg.protocols[i].alpn_protocols_len)
tls_data_set_list(cfg.protocols[i].data, 1,
(const char**) cfg.protocols[i].alpn_protocols,
cfg.protocols[i].alpn_protocols_len);
}
p->timeouts.head = NULL;
p->timeouts.tail = NULL;
}
}
void config_sanity_check(struct sslhcfg_item* cfg)
{
size_t i;
/* If compiling with systemd socket support no need to require listen address */
#ifndef SYSTEMD
if (!cfg->listen_len && !cfg->inetd) {
print_message(msg_config_error, "No listening address specified; use at least one -p option\n");
exit(1);
}
#endif
for (i = 0; i < cfg->protocols_len; ++i) {
if (strcmp(cfg->protocols[i].name, "tls")) {
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",
cfg->protocols[i].name, cfg->protocols[i].host, cfg->protocols[i].port);
exit(1);
}
if (cfg->protocols[i].alpn_protocols_len) {
print_message(msg_config_error, "name: \"%s\"; host: \"%s\"; port: \"%s\": "
"Config option alpn_protocols is only applicable for tls\n",
cfg->protocols[i].name, cfg->protocols[i].host, cfg->protocols[i].port);
exit(1);
}
}
if (cfg->protocols[i].is_udp) {
if (cfg->protocols[i].tfo_ok) {
print_message(msg_config_error, "name: \"%s\"; host: \"%s\"; port: \"%s\": "
"Config option tfo_ok is not applicable for udp connections\n",
cfg->protocols[i].name, cfg->protocols[i].host, cfg->protocols[i].port);
exit(1);
}
} else {
if (!strcmp(cfg->protocols[i].name, "wireguard")) {
print_message(msg_config_error, "Wireguard works only with UDP\n");
exit(1);
}
}
}
}
int main(int argc, char *argv[], char* envp[])
{
extern char *optarg;
extern int optind;
int res, num_addr_listen;
struct listen_endpoint *listen_sockets;
#ifdef LIBBSD
setproctitle_init(argc, argv, envp);
#endif
memset(&cfg, 0, sizeof(cfg));
res = sslhcfg_cl_parse(argc, argv, &cfg);
if (res) exit(6);
if (cfg.version) {
printf("%s %s\n", server_type, VERSION);
exit(0);
}
config_protocols();
config_sanity_check(&cfg);
if (cfg.inetd)
{
close(fileno(stderr)); /* Make sure no error will go to client */
start_shoveler(0);
exit(0);
}
printsettings();
num_addr_listen = start_listen_sockets(&listen_sockets);
#ifdef SYSTEMD
if (num_addr_listen < 1) {
print_message(msg_config_error, "No listening sockets found, restart sockets or specify addresses in config\n");
exit(1);
}
#endif
if (!cfg.foreground) {
if (fork() > 0) exit(0); /* Detach */
/* New session -- become group leader */
if (getuid() == 0) {
res = setsid();
CHECK_RES_DIE(res, "setsid: already process leader");
}
}
setup_signals();
if (cfg.pidfile)
write_pid_file(cfg.pidfile);
/* Open syslog connection before we drop privs/chroot */
setup_syslog(argv[0]);
/* Open log file for writing */
setup_logfile();
if (cfg.user || cfg.chroot)
drop_privileges(cfg.user, cfg.chroot);
printcaps();
print_message(msg_config, "%s %s started\n", server_type, VERSION);
main_loop(listen_sockets, num_addr_listen);
close_logfile();
free(listen_sockets);
return 0;
}
+217
View File
@@ -0,0 +1,217 @@
/*
sslh-select: mono-processus server
# Copyright (C) 2007-2021 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
*/
/* Why use select(2) rather than poll(2)?
* No real reason except that's how it was written at first. This article:
* https://daniel.haxx.se/docs/poll-vs-select.html suggests that over a few
* hundred file descriptors, both become very slow, so there is little
* incentive to move to poll() to support more than FD_SETSIZE (which is 1024
* on many Linux. To support large numbers of descriptors efficiently, either use sslh-fork
* or sslh-ev. */
#define __LINUX__
#include "common.h"
#include "probe.h"
#include "tcp-listener.h"
#include "udp-listener.h"
#include "collection.h"
#include "processes.h"
#include "gap.h"
#include "log.h"
const char* server_type = "sslh-select";
/* watcher type for a select() loop */
struct watchers {
fd_set fds_r, fds_w; /* reference fd sets (used to init working copies) */
int max_fd; /* Highest fd number to pass to select() */
};
static void watchers_init(watchers** w, struct listen_endpoint* listen_sockets,
int num_addr_listen)
{
*w = malloc(sizeof(**w));
CHECK_ALLOC(*w, "malloc");
memset(*w, 0, sizeof(**w));
FD_ZERO(&(*w)->fds_r);
FD_ZERO(&(*w)->fds_w);
for (int i = 0; i < num_addr_listen; i++) {
watchers_add_read(*w, listen_sockets[i].socketfd);
set_nonblock(listen_sockets[i].socketfd);
}
}
void watchers_add_read(watchers* w, int fd)
{
FD_SET(fd, &w->fds_r);
if (fd > w->max_fd)
w->max_fd = fd + 1;
}
void watchers_del_read(watchers* w, int fd)
{
FD_CLR(fd, &w->fds_r);
}
void watchers_add_write(watchers* w, int fd)
{
FD_SET(fd, &w->fds_w);
if (fd > w->max_fd)
w->max_fd = fd + 1;
}
void watchers_del_write(watchers* w, int fd)
{
FD_CLR(fd, &w->fds_w);
}
/* /end watchers */
/* if fd becomes higher than FD_SETSIZE, things won't work so well with FD_SET
* and FD_CLR. Need to drop connections if we go above that limit */
static int fd_out_of_range(int fd) {
if (fd >= FD_SETSIZE) {
print_message(msg_system_error, "too many open file descriptor to monitor them all -- dropping connection\n");
return 1;
}
return 0;
}
/* Main loop: the idea is as follow:
* - fds_r and fds_w contain the file descriptors to monitor in read and write
* - When a file descriptor goes off, process it: read from it, write the data
* to its corresponding pair.
* - When a file descriptor blocks when writing, remove the read fd from fds_r,
* move the data to a deferred buffer, and add the write fd to fds_w. Deferred
* buffer is allocated dynamically.
* - When we can write to a file descriptor that has deferred data, we try to
* write as much as we can. Once all data is written, remove the fd from fds_w
* and add its corresponding pair to fds_r, free the buffer.
*
* That way, each pair of file descriptor (read from one, write to the other)
* is monitored either for read or for write, but never for both.
*/
void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen)
{
struct loop_info fd_info = {0};
fd_set readfds, writefds; /* working read and write fd sets */
struct timeval tv;
int i, res;
fd_info.num_probing = 0;
fd_info.probing_list = gap_init(0);
udp_init(&fd_info);
tcp_init();
watchers_init(&fd_info.watchers, listen_sockets, num_addr_listen);
fd_info.collection = collection_init(fd_info.watchers->max_fd);
while (1)
{
memset(&tv, 0, sizeof(tv));
tv.tv_sec = cfg.timeout;
memcpy(&readfds, &fd_info.watchers->fds_r, sizeof(readfds));
memcpy(&writefds, &fd_info.watchers->fds_w, sizeof(writefds));
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,
NULL, fd_info.num_probing ? &tv : NULL);
if (res < 0)
perror("select");
/* Check main socket for new connections */
for (i = 0; i < num_addr_listen; i++) {
if (FD_ISSET(listen_sockets[i].socketfd, &readfds)) {
struct connection* new_cnx = cnx_accept_process(&fd_info, &listen_sockets[i]);
if (fd_out_of_range(new_cnx->q[0].fd))
tidy_connection(new_cnx, &fd_info);
/* don't also process it as a read socket */
FD_CLR(listen_sockets[i].socketfd, &readfds);
}
}
/* Check all sockets for write activity */
for (i = 0; i < fd_info.watchers->max_fd; i++) {
/* Check if it's active AND currently monitored (if a connection
* died, it gets tidied, which closes both sockets, but writefs does
* not know about that */
if (FD_ISSET(i, &writefds) && FD_ISSET(i, &fd_info.watchers->fds_w)) {
cnx_write_process(&fd_info, i);
}
}
/* Check sockets in probing state for timeouts */
for (i = 0; i < fd_info.num_probing; i++) {
struct connection* cnx = gap_get(fd_info.probing_list, i);
if (!cnx || cnx->state != ST_PROBING) {
print_message(msg_int_error, "Inconsistent probing: cnx=0x%p\n", cnx);
if (cnx)
print_message(msg_int_error, "Inconsistent probing: state=%d\n", cnx->state);
exit(1);
}
if (cnx->probe_timeout < time(NULL)) {
print_message(msg_fd, "timeout slot %d\n", i);
probing_read_process(cnx, &fd_info);
}
}
/* Check all sockets for read activity */
for (i = 0; i < fd_info.watchers->max_fd; i++) {
/* Check if it's active AND currently monitored (if a connection
* died, it gets tidied, which closes both sockets, but readfs does
* not know about that */
if (FD_ISSET(i, &readfds) && FD_ISSET(i, &fd_info.watchers->fds_r)) {
cnx_read_process(&fd_info, i);
}
}
}
}
void start_shoveler(int listen_socket) {
print_message(msg_config_error, "inetd mode is not supported in select mode\n");
exit(1);
}
/* The actual main is in common.c: it's the same for both version of
* the server
*/
-469
View File
@@ -1,469 +0,0 @@
/*
Reimplementation of sslh in C
# Copyright (C) 2007-2008 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
*/
#define VERSION "1.5"
#include <sys/types.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <pwd.h>
#include <syslog.h>
#ifdef LIBWRAP
#include <tcpd.h>
int allow_severity =0, deny_severity = 0;
#endif
#define CHECK_RES_DIE(res, str) \
if (res == -1) { \
perror(str); \
exit(1); \
}
#define USAGE_STRING \
"sslh v" VERSION "\n" \
"usage:\n" \
"\texport PIDFILE=/var/run/sslhc.pid\n" \
"\tsslh [-t <timeout>] -u <username> -p [listenaddr:]<listenport> \n" \
"\t\t-s [sshhost:]port -l [sslhost:]port [-v]\n\n" \
"-v: verbose\n" \
"-p: address and port to listen on. default: 0.0.0.0:443\n" \
"-s: SSH address: where to connect an SSH connection. default: localhost:22\n" \
"-l: SSL address: where to connect an SSL connection.\n" \
""
int verbose = 0; /* That's really quite global */
/* Starts a listening socket on specified address.
Returns file descriptor
*/
int start_listen_socket(struct sockaddr *addr)
{
struct sockaddr_in *saddr = (struct sockaddr_in*)addr;
int sockfd, res, reuse;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
CHECK_RES_DIE(sockfd, "socket");
reuse = 1;
res = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&reuse, sizeof(reuse));
CHECK_RES_DIE(res, "setsockopt");
res = bind (sockfd, (struct sockaddr*)saddr, sizeof(*saddr));
CHECK_RES_DIE(res, "bind");
res = listen (sockfd, 5);
CHECK_RES_DIE(res, "listen");
return sockfd;
}
/*
* moves data from one fd to other
* returns 0 if incoming socket closed, size moved otherwise
*/
int fd2fd(int target, int from)
{
char buffer[BUFSIZ];
int size;
size = read(from, buffer, sizeof(buffer));
CHECK_RES_DIE(size, "read");
if (size == 0)
return 0;
size = write(target, buffer, size);
CHECK_RES_DIE(size, "write");
return size;
}
/* shovels data from one fd to the other and vice-versa
returns after one socket closed
*/
int shovel(int fd1, int fd2)
{
fd_set fds;
int res;
FD_ZERO(&fds);
while (1) {
FD_SET(fd1, &fds);
FD_SET(fd2, &fds);
res = select(
(fd1 > fd2 ? fd1 : fd2 ) + 1,
&fds,
NULL,
NULL,
NULL
);
CHECK_RES_DIE(res, "select");
if (FD_ISSET(fd1, &fds)) {
res = fd2fd(fd2, fd1);
if (!res) {
if (verbose) fprintf(stderr, "client socket closed\n");
return res;
}
}
if (FD_ISSET(fd2, &fds)) {
res = fd2fd(fd1, fd2);
if (!res) {
if (verbose) fprintf(stderr, "server socket closed\n");
return res;
}
}
}
}
/* returns a string that prints the IP and port of the sockaddr */
char* sprintaddr(char* buf, size_t size, struct sockaddr* s)
{
char addr_str[1024];
inet_ntop(AF_INET, &((struct sockaddr_in*)s)->sin_addr, addr_str, sizeof(addr_str));
snprintf(buf, size, "%s:%d", addr_str, ntohs(((struct sockaddr_in*)s)->sin_port));
return buf;
}
/* turns a "hostname:port" string into a struct sockaddr;
sock: socket address to which to copy the addr
fullname: input string -- it gets clobbered
*/
void resolve_name(struct sockaddr *sock, char* fullname) {
struct addrinfo *addr, hint;
char *serv, *host;
int res;
char *sep = strchr(fullname, ':');
if (!sep) /* No separator: parameter is just a port */
{
serv = fullname;
fprintf(stderr, "names must be fully specified as hostname:port\n");
exit(1);
}
else {
host = fullname;
serv = sep+1;
*sep = 0;
}
memset(&hint, 0, sizeof(hint));
hint.ai_family = PF_INET;
hint.ai_socktype = SOCK_STREAM;
res = getaddrinfo(host, serv, &hint, &addr);
if (res) {
fprintf(stderr, "%s\n", gai_strerror(res));
if (res == EAI_SERVICE)
fprintf(stderr, "(Check you have specified all ports)\n");
exit(1);
}
memcpy(sock, addr->ai_addr, sizeof(*sock));
}
/* syslogs who connected to where */
void log_connection(int socket, char* target)
{
struct sockaddr peeraddr;
socklen_t size = sizeof(peeraddr);
char buf[64];
int res;
res = getpeername(socket, &peeraddr, &size);
CHECK_RES_DIE(res, "getpeername");
syslog(LOG_INFO, "connection from %s forwarded to %s\n",
sprintaddr(buf, sizeof(buf), &peeraddr), target);
}
/*
* Settings that depend on the command line. That's less global than verbose * :-)
* They're set in main(), but also used in start_shoveler(), and it'd be heavy-handed
* to pass it all as parameters
*/
int timeout = 2;
struct sockaddr addr_listen;
struct sockaddr addr_ssl, addr_ssh;
/* libwrap (tcpd): check the ssh connection is legal. This is necessary because
* the actual sshd will only see a connection coming from localhost and can't
* apply the rules itself.
*/
void check_access_rights(int in_socket)
{
#ifdef LIBWRAP
struct sockaddr peeraddr;
socklen_t size = sizeof(peeraddr);
char addr_str[1024];
struct hostent *host;
struct in_addr addr;
int res;
res = getpeername(in_socket, &peeraddr, &size);
CHECK_RES_DIE(res, "getpeername");
inet_ntop(AF_INET, &((struct sockaddr_in*)&peeraddr)->sin_addr, addr_str, sizeof(addr_str));
addr.s_addr = inet_addr(addr_str);
host = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET);
if (!hosts_ctl("sshd", (host ? host->h_name : STRING_UNKNOWN), addr_str, STRING_UNKNOWN)) {
if (verbose)
fprintf(stderr, "access denied\n");
log_connection(in_socket, "access denied");
close(in_socket);
exit(0);
}
#endif
}
/* Child process that finds out what to connect to and proxies
*/
void start_shoveler(int in_socket)
{
fd_set fds;
struct timeval tv;
struct sockaddr *saddr;
int res;
int out_socket;
char *target;
FD_ZERO(&fds);
FD_SET(in_socket, &fds);
memset(&tv, 0, sizeof(tv));
tv.tv_sec = timeout;
res = select(in_socket + 1, &fds, NULL, NULL, &tv);
if (res == -1)
perror("select");
/* Pick the target address depending on whether we timed out or not */
if (FD_ISSET(in_socket, &fds)) {
/* The client wrote something to the socket: it's an SSL connection */
saddr = &addr_ssl;
target = "SSL";
} else {
/* The client hasn't written anything and we timed out: connect to SSH */
saddr = &addr_ssh;
target = "SSH";
/* do hosts_access check if built with libwrap support */
check_access_rights(in_socket);
}
log_connection(in_socket, target);
/* Connect the target socket */
out_socket = socket(AF_INET, SOCK_STREAM, 0);
res = connect(out_socket, saddr, sizeof(addr_ssl));
CHECK_RES_DIE(res, "connect");
if (verbose)
fprintf(stderr, "connected to something\n");
shovel(in_socket, out_socket);
close(in_socket);
close(out_socket);
if (verbose)
fprintf(stderr, "connection closed down\n");
exit(0);
}
/* SIGCHLD handling:
* we need to reap our children
*/
void child_handler(int signo)
{
signal(SIGCHLD, &child_handler);
wait(NULL);
}
void setup_signals(void)
{
void* res;
res = signal(SIGCHLD, &child_handler);
if (res == SIG_ERR) {
perror("signal");
exit(1);
}
}
/* We don't want to run as root -- drop priviledges if required */
void drop_privileges(char* user_name)
{
int res;
struct passwd *pw = getpwnam(user_name);
if (!pw) {
fprintf(stderr, "%s: not found\n", user_name);
exit(1);
}
if (verbose)
fprintf(stderr, "turning into %s\n", user_name);
res = setgid(pw->pw_gid);
CHECK_RES_DIE(res, "setgid");
setuid(pw->pw_uid);
CHECK_RES_DIE(res, "setuid");
}
/* Writes my PID if $PIDFILE is defined */
void write_pid_file(void)
{
char *pidfile = getenv("PIDFILE");
FILE *f;
if (!pidfile)
return;
f = fopen(pidfile, "w");
if (!f) {
perror(pidfile);
exit(1);
}
fprintf(f, "%d\n", getpid());
fclose(f);
}
void printsettings(void)
{
char buf[64];
fprintf(
stderr,
"SSL addr: %s (after timeout %ds)\n",
sprintaddr(buf, sizeof(buf), &addr_ssl),
timeout
);
fprintf(stderr, "SSH addr: %s\n", sprintaddr(buf, sizeof(buf), &addr_ssh));
fprintf(stderr, "listening on %s\n", sprintaddr(buf, sizeof(buf), &addr_listen));
}
int main(int argc, char *argv[])
{
extern char *optarg;
extern int optind;
int c, res;
int in_socket, listen_socket;
/* Init defaults */
char *user_name = "nobody";
char listen_str[] = "0.0.0.0:443";
char ssl_str[] = "localhost:442";
char ssh_str[] = "localhost:22";
resolve_name(&addr_listen, listen_str);
resolve_name(&addr_ssl, ssl_str);
resolve_name(&addr_ssh, ssh_str);
while ((c = getopt(argc, argv, "t:l:s:p:vu:")) != EOF) {
switch (c) {
case 't':
timeout = atoi(optarg);
break;
case 'p':
resolve_name(&addr_listen, optarg);
break;
case 'l':
resolve_name(&addr_ssl, optarg);
break;
case 's':
resolve_name(&addr_ssh, optarg);
break;
case 'v':
verbose += 1;
break;
case 'u':
user_name = optarg;
break;
default:
fprintf(stderr, USAGE_STRING);
exit(2);
}
}
if (verbose)
printsettings();
setup_signals();
listen_socket = start_listen_socket(&addr_listen);
if (fork() > 0) exit(0); /* Detach */
write_pid_file();
drop_privileges(user_name);
/* New session -- become group leader */
res = setsid();
CHECK_RES_DIE(res, "setsid: already process leader");
/* Open syslog connection */
openlog(argv[0], LOG_CONS, LOG_AUTH);
/* Main server loop: accept connections, find what they are, fork shovelers */
while (1)
{
in_socket = accept(listen_socket, 0, 0);
if (verbose) fprintf(stderr, "accepted fd %d\n", in_socket);
if (!fork())
{
start_shoveler(in_socket);
exit(0);
}
close(in_socket);
}
return 0;
}
+241
View File
@@ -0,0 +1,241 @@
# I'm just not gonna write troff :-)
=head1 NAME
sslh - protocol demultiplexer
=head1 SYNOPSIS
sslh [B<-F>I<config file>] [B<-t> I<num>] [B<--transparent>] [B<-p> I<listening address> [B<-p> I<listening address> ...] [B<--tls> I<target address for TLS>] [B<--ssh> I<target address for SSH>] [B<--openvpn> I<target address for OpenVPN>] [B<--http> I<target address for HTTP>] [B<--xmpp> I<target address for XMPP>] [B<--tinc> I<target address for TINC>] [B<--anyprot> I<default target address>] [B<--on-timeout> I<protocol name>] [B<-u> I<username>] [B<-C> I<chroot>] [B<-P> I<pidfile>] [-v] [-i] [-V] [-f] [-n]
=head1 DESCRIPTION
B<sslh> accepts connections on specified ports, and forwards
them further based on tests performed on the first data
packet sent by the remote client.
Probes for HTTP, SSL, SSH, OpenVPN, tinc, XMPP are
implemented, and any other protocol that can be tested using
a regular expression, can be recognised. A typical use case
is to allow serving several services on port 443 (e.g. to
connect to ssh from inside a corporate firewall, which
almost never block port 443) while still serving HTTPS on
that port.
Hence B<sslh> acts as a protocol demultiplexer, or a
switchboard. Its name comes from its original function to
serve SSH and HTTPS on the same port.
=head2 Libwrap support
One drawback of B<sslh> is that the servers do not see the
original IP address of the client anymore, as the connection
is forwarded through B<sslh>.
For this reason, B<sslh> can be compiled with B<libwrap> to
check accesses defined in F</etc/hosts.allow> and
F</etc/hosts.deny>. Libwrap services can be defined using
the configuration file.
=head2 Configuration file
A configuration file can be supplied to B<sslh>. Command
line arguments override file settings. B<sslh> uses
B<libconfig> to parse the configuration file, so the general
file format is indicated in
L<http://www.hyperrealm.com/libconfig/libconfig_manual.html>.
Please refer to the example configuration file provided with
B<sslh> for the specific format (Options have the same names
as on the command line, except for the list of listen ports
and the list of protocols).
The configuration file makes it possible to specify
protocols using regular expressions: a list of regular
expressions is given as the I<regex_patterns> parameter, and if the
first packet received from the client matches any of these
expressions, B<sslh> connects to that protocol.
=head2 Probing protocols
When receiving an incoming connection, B<sslh> will read the
first bytes sent by the connecting client. It will then
probe for the protocol in the order specified on the command
line (or the configuration file). Therefore B<--anyprot>
should alway be used last, as it always succeeds and further
protocols will never be tried.
If no data is sent by the client, B<sslh> will eventually
time out and connect to the protocol specified with
B<--on-timeout>, or I<ssh> if none is specified.
=head2 Logging
As a security/authorization program, B<sslh> logs to the
LOG_AUTH facility, with priority LOG_INFO for normal
connections and LOG_ERR for failures.
=head1 OPTIONS
=over 4
=item B<-F>I<filename>, B<--config> I<filename>
Uses I<filename> as configuration file. If other
command-line options are specified, they will override the
configuration file's settings.
=item B<-t> I<num>, B<--timeout> I<num>
Timeout before forwarding the connection to the timeout
protocol (which should usually be SSH). Default is 2s.
=item B<--on-timeout> I<protocol name>
Name of the protocol to connect to after the timeout period
is over. Default is to forward to the first specified
protocol. It usually makes sense to specify 'ssh' as the
timeout protocol, as the SSH specification does not tell
who is supposed to speak first and a large number of SSH
clients wait for the server to send its banner.
=item B<--transparent>
Makes B<sslh> behave as a transparent proxy, i.e. the
receiving service sees the original client's IP address.
This works on Linux only and involves B<iptables> settings.
Refer to the README for more information.
=item B<-p> I<listening address>, B<--listen> I<listening address>
Interface and port on which to listen, e.g. I<foobar:443>,
where I<foobar> is the name of an interface (typically the
IP address on which the Internet connection ends up).
This can be specified several times to bind B<sslh> to
several addresses.
=item B<--tls> I<target address>
Interface and port on which to forward SSL connection,
typically I<localhost:443>.
Note that you can set B<sslh> to listen on I<ext_ip:443> and
B<httpd> to listen on I<localhost:443>: this allows clients
inside your network to just connect directly to B<httpd>.
Also, B<sslh> probes for SSLv3 (or TLSv1) handshake and will
reject connections from clients requesting SSLv2. This is
compliant with RFC6176 which prohibits the usage of SSLv2. If
you wish to accept SSLv2, use B<--anyprot> instead.
=item B<--ssh> I<target address>
Interface and port on which to forward SSH connections,
typically I<localhost:22>.
=item B<--openvpn> I<target address>
Interface and port on which to forward OpenVPN connections,
typically I<localhost:1194>.
=item B<--xmpp> I<target address>
Interface and port on which to forward XMPP connections,
typically I<localhost:5222>.
=item B<--http> I<target address>
Interface and port on which to forward HTTP connections,
typically I<localhost:80>.
=item B<--tinc> I<target address>
Interface and port on which to forward tinc connections,
typically I<localhost:655>.
This is experimental. If you use this feature, please report
the results (even if it works!)
=item B<--anyprot> I<target address>
Interface and port on which to forward if no other protocol
has been found. Because B<sslh> tries protocols in the order
specified on the command line, this should be specified
last. If no default is specified, B<sslh> will forward
unknown protocols to the first protocol specified.
=item B<-v>, B<--verbose>
Increase verboseness.
=item B<-n>, B<--numeric>
Do not attempt to resolve hostnames: logs will contain IP
addresses. This is mostly useful if the system's DNS is slow
and running the I<sslh-select> variant, as DNS requests will
hang all connections.
=item B<-V>
Prints B<sslh> version.
=item B<-u> I<username>, B<--user> I<username>
Requires to run under the specified username.
=item B<-C> I<chroot>, B<--chroot> I<chroot>
Requires to run under the specified chroot.
=item B<-P> I<pidfile>, B<--pidfile> I<pidfile>
Specifies a file in which to write the PID of the main
server.
=item B<-i>, B<--inetd>
Runs as an I<inetd> server. Options B<-P> (PID file), B<-p>
(listen address), B<-u> (user) are ignored.
=item B<-f>, B<--foreground>
Runs in foreground. The server will not fork and will remain connected
to the terminal. Messages normally sent to B<syslog> will also be sent
to I<stderr>.
=item B<--background>
Runs in background. This overrides B<foreground> if set in
the configuration file (or on the command line, but there is
no point setting both on the command line unless you have a
personality disorder).
=back
=head1 FILES
=over 4
=item F</etc/init.d/sslh>
Start-up script. The standard actions B<start>, B<stop> and
B<restart> are supported.
=item F</etc/default/sslh>
Server configuration. These are environment variables
loaded by the start-up script and passed to B<sslh> as
command-line arguments. Refer to the OPTIONS section for a
detailed explanation of the variables used by B<sslh>.
=back
=head1 SEE ALSO
The latest version is available from
L<http://www.rutschle.net/tech/sslh>, and can be tracked
from L<http://freecode.com/projects/sslh>.
=head1 AUTHOR
Written by Yves Rutschle.
+302
View File
@@ -0,0 +1,302 @@
# This is a configuration file for the configuration
# generator: it is used by
# conf2struct(http://www.rutschle.net/tech/conf2struct/README.html)
# to generate the config file and command line parser in
# sslh-conf.c
header: "sslh-conf.h";
parser: "sslh-conf.c";
printer: true;
conffile_option: ("F", "config");
# List of includes to define runtime types
# (bug in libconfig? if swallows the brackets if they start
# the string)
includes: (
"probe.h",
" <sys/types.h>",
" <sys/socket.h>",
" <netdb.h>"
);
config: {
name : "sslhcfg",
type: "list",
items: (
{ name: "verbose-config"; type: "int"; default: 0;
description: "Print configuration at startup" },
{ name: "verbose-config-error"; type: "int"; default: 3;
description: "Print configuration errors" },
{ name: "verbose-connections"; type: "int"; default: 3;
description: "Trace established incoming address to forward address" },
{ name: "verbose-connections-try"; type: "int"; default: 0;
description: "Connection errors" },
{ name: "verbose-connections-error"; type: "int"; default: 3;
description: "Connection attempts towards targets" },
{ name: "verbose-fd"; type: "int"; default: 0;
description: "File descriptor activity, open/close/whatnot" },
{ name: "verbose-packets"; type: "int"; default: 0;
description: "Hexdump packets on which probing is done" },
{ name: "verbose-probe-info"; type: "int"; default: 0;
description: "Trace the probe process" },
{ name: "verbose-probe-error"; type: "int"; default: 3;
description: "Failures and problems during probing" },
{ name: "verbose-system-error"; type: "int"; default: 3;
description: "System call failures" },
{ name: "verbose-int-error"; type: "int"; default: 3;
description: "Internal errors that should never happen" },
{ name: "version"; type: "bool"; default: false;
short: "V";
description: "Print version information and exit"; },
{ name: "foreground"; type: "bool"; default: false;
short: "f";
description: "Run in foreground instead of as a daemon"; },
{ name: "inetd"; type: "bool"; default: false;
short: "i";
description: "Run in inetd mode: use stdin/stdout instead of network listen"; },
{ name: "numeric"; type: "bool"; default: false;
short: "n";
description: "Print IP addresses and ports as numbers"; },
{ name: "transparent"; type: "bool"; default: false;
description: "Set up as a transparent proxy"; },
{ name: "timeout"; type: "int"; default: 5;
short: "t";
description: "Set up timeout before connecting to default target"; },
{ name: "udp_max_connections"; type: "int"; default: 1024;
description: "Number of concurrent UDP connections"; },
{ name: "user"; type: "string"; optional: true;
short: "u";
description: "Username to change to after set-up"; },
{ name: "pidfile"; type: "string"; optional: true;
short: "P"; argdesc: "<file>";
description: "Path to file to store PID of current instance"; },
{ name: "chroot"; type: "string"; optional: true;
short: "C"; argdesc: "<path>";
description: "Root to change to after set-up"; },
{ name: "syslog_facility"; type: "string"; default: "auth";
description: "Facility to syslog to"; },
{ name: "logfile"; type: "string"; optional: true;
description: "Log messages to a file" },
{ name: "on-timeout"; type: "string"; default: "ssh";
description: "Target to connect to when timing out"; },
{ name: "prefix"; type: "string"; default: ""; description: "Reserved for testing" }, # For echosrv only, not sslh
{ name: "listen",
no_cl_accessors: true; # disable generation of individual cl options for each group element (we create a specific --listen option further below)
type: "list",
items: (
{ name: "host"; type: "string"; var: true; },
{ name: "port"; type: "string"; var: true; },
{ name: "is_udp"; type: "bool"; default: false },
{ name: "keepalive"; type: "bool"; default: false; }
)
},
{ name: "protocols",
no_cl_accessors: true;
type: "list",
items: (
{ name: "name"; type: "string"; },
{ name: "host"; type: "string"; var: true; },
{ name: "port"; type: "string"; var: true; },
{ name: "service"; type: "string"; optional: true; },
{ name: "is_udp"; type: "bool"; default: false },
{ name: "udp_timeout"; type: "int"; default: 60 },
{ name: "fork"; type: "bool"; default: false },
{ name: "tfo_ok"; type: "bool"; default: false;
description: "Set to true if this protocol supports TCP FAST OPEN" },
{ name: "transparent"; type: "bool"; default: false;
description: "Set to proxy this protocol transparently" },
{ name: "resolve_on_forward"; type: "bool"; default: false;
description: "Set to true if server address should be resolved on (every) newly incoming connection (again)" },
{ name: "log_level"; type: "int"; default: 1 },
{ name: "keepalive"; type: "bool"; default: false },
{ name: "sni_hostnames",
type: "array",
element_type: "string"
},
{ name: "alpn_protocols",
type: "array",
element_type: "string"
},
{ name: "regex_patterns",
type: "array",
element_type: "string"
},
{ name: "minlength"; type: "int"; optional: true },
# Runtime data
{ name: "probe"; type: "runtime"; c_type: "T_PROBE*" },
{ name: "saddr"; type: "runtime"; c_type: "struct addrinfo*" },
{ name: "data"; type: "runtime"; c_type: "void*" },
{ name: "timeouts"; type: "runtime"; c_type: "dl_list" }
)
}
)
}
# Command line for list settings: additional options that
# can set up several settings at once. Each option will
# create a new group setting entry if required (with
# defaults set up)
# This only works with string targets
# This may not be the right abstraction at all and way too
# sslh-centric
cl_groups: (
{ name: "listen"; pattern: "(.+):(\w+)"; description: "Listen on host:port";
short: "p"; argdesc: "<host:port>";
list: "listen";
# no override, this just adds to the list (and thus can be specified several times)
targets: (
{ path: "host"; value: "$1" },
{ path: "port"; value: "$2" }
);
},
{ name: "ssh"; pattern: "(.+):(\w+)"; description: "Set up ssh target";
list: "protocols"; # List name that we're defining with this command line option
override: "name"; # Field in the group to override. If not found in list, add an item
# (it's mandatory to have that field as one of the targets
# below)
argdesc: "<host:port>";
targets: (
{ path: "name"; value: "ssh" },
{ path: "host"; value: "$1" },
{ path: "port"; value: "$2" },
{ path: "fork"; value: 1 },
{ path: "log_level"; value: 1 },
{ path: "tfo_ok"; value: 1 }
);
},
{ name: "tls"; 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";
argdesc: "<host:port>";
targets: (
{ path: "name"; value: "openvpn" },
{ path: "host"; value: "$1" },
{ path: "port"; value: "$2" },
{ path: "log_level"; value: 1 },
{ path: "tfo_ok"; value: 1 }
);
},
{ name: "tinc"; pattern: "(.+):(\w+)"; description: "Set up tinc target";
list: "protocols";
override: "name";
argdesc: "<host:port>";
targets: (
{ path: "name"; value: "tinc" },
{ path: "host"; value: "$1" },
{ path: "port"; value: "$2" },
{ path: "log_level"; value: 1 },
{ path: "tfo_ok"; value: 1 }
);
},
{ name: "wireguard"; pattern: "(.+):(\w+)"; description: "Set up WireGuard target";
list: "protocols";
override: "name";
argdesc: "<host:port>";
targets: (
{ path: "name"; value: "wireguard" },
{ path: "host"; value: "$1" },
{ path: "port"; value: "$2" },
{ path: "log_level"; value: 1 },
{ path: "tfo_ok"; value: 1 }
);
},
{ name: "xmpp"; pattern: "(.+):(\w+)"; description: "Set up XMPP target";
list: "protocols";
override: "name";
argdesc: "<host:port>";
targets: (
{ path: "name"; value: "xmpp" },
{ path: "host"; value: "$1" },
{ path: "port"; value: "$2" },
{ path: "log_level"; value: 1 }
);
},
{ name: "http"; pattern: "(.+):(\w+)"; description: "Set up HTTP (plain) target";
list: "protocols";
override: "name";
argdesc: "<host:port>";
targets: (
{ path: "name"; value: "http" },
{ path: "host"; value: "$1" },
{ path: "port"; value: "$2" },
{ path: "log_level"; value: 1 }
);
},
{ name: "adb"; pattern: "(.+):(\w+)"; description: "Set up ADB (Android Debug) target";
list: "protocols";
override: "name";
argdesc: "<host:port>";
targets: (
{ path: "name"; value: "adb" },
{ path: "host"; value: "$1" },
{ path: "port"; value: "$2" },
{ path: "log_level"; value: 1 }
);
},
{ name: "socks5"; pattern: "(.+):(\w+)"; description: "Set up socks5 target";
list: "protocols";
override: "name";
argdesc: "<host:port>";
targets: (
{ path: "name"; value: "socks5" },
{ path: "host"; value: "$1" },
{ path: "port"; value: "$2" },
{ path: "log_level"; value: 1 }
);
},
{ name: "syslog"; pattern: "(.+):(\w+)"; description: "Set up syslog target";
list: "protocols";
override: "name";
argdesc: "<host:port>";
targets: (
{ path: "name"; value: "syslog" },
{ path: "host"; value: "$1" },
{ path: "port"; value: "$2" },
{ path: "log_level"; value: 1 }
);
},
{ name: "msrdp"; pattern: "(.+):(\w+)"; description: "Set up msrdp target";
list: "protocols";
override: "name";
argdesc: "<host:port>";
targets: (
{ path: "name"; value: "msrdp" },
{ path: "host"; value: "$1" },
{ path: "port"; value: "$2" },
{ path: "log_level"; value: 1 }
);
},
{ name: "anyprot"; pattern: "(.+):(\w+)"; description: "Set up default target";
list: "protocols";
override: "name";
argdesc: "<host:port>";
targets: (
{ path: "name"; value: "anyprot" },
{ path: "host"; value: "$1" },
{ path: "port"; value: "$2" },
{ path: "log_level"; value: 1 }
);
}
)
+159
View File
@@ -0,0 +1,159 @@
#include <libconfig.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "common.h"
#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");
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",
filename,
config_error_line(&config),
config_error_text(&config));
return -1;
}
} 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++) {
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",
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);
}
}
}
}
return len;
}
static int write_socket_unit(FILE *socket, char *listen[], int num_addr, const char *source) {
int i;
fprintf(socket,
"# Automatically generated by systemd-sslh-generator\n\n"
"[Unit]\n"
"Before=sslh.service\n"
"SourcePath=%s\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++) {
fprintf(socket, "ListenStream=%s\n", listen[i]);
}
return 0;
}
static int gen_sslh_config(char *runtime_unit_dir) {
char *sslh_conf;
int num_addr;
FILE *config;
char **listen;
FILE *runtime_conf_fd = stdout;
const char *unit_file;
/* 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;
}
}
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);
}
return write_socket_unit(runtime_conf_fd, listen, num_addr, sslh_conf);
}
int main(int argc, char *argv[]){
int r = 0;
int k;
char *runtime_unit_dest = "";
if (argc > 1 && (argc != 4) ) {
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;
}
Executable
+475
View File
@@ -0,0 +1,475 @@
#! /usr/bin/perl -w
# Test script for sslh
# Uses Conf::Libconfig to read sslh config file: install
# with:
# cpan Conf::Libconfig
use strict;
use IO::Socket::INET6;
use Test::More qw/no_plan/;
use Conf::Libconfig;
my $conf = new Conf::Libconfig;
$conf->read_file("test.cfg");
my $no_listen = 8083; # Port on which no-one listens
my $pidfile = $conf->lookup_value("pidfile");
my $sslh_port = $conf->fetch_array("listen")->[0]->{port};
my $user = (getpwuid $<)[0]; # Run under current username
# Which tests do we run
my $SSH_SHY_CNX = 0;
my $PROBES_NOFRAG = 1;
my $PROBES_AGAIN = 1;
my $SSL_MIX_SSH = 1;
my $SSH_MIX_SSL = 1;
my $DROP_CNX = 1;
# Robustness tests. These are mostly to achieve full test
# coverage, but do not necessarily result in an actual test
# (e.g. some tests need to be run with valgrind to check all
# memory management code).
my $RB_CNX_NOSERVER = 0;
my $RB_PARAM_NOHOST = 0;
my $RB_WRONG_USERNAME = 0;
my $RB_OPEN_PID_FILE = 0;
my $RB_RESOLVE_ADDRESS = 0;
my $RB_CL_PARAMS = 0;
`lcov --directory . --zerocounters`;
sub verbose_exec
{
my ($cmd) = @_;
warn "$cmd\n";
if (!fork) {
exec $cmd;
}
}
# We want to keep track of tests to print a report at the
# end, so we centralise all calls to Test::More::is here
my $cnt = 1; # test counter
my @results;
sub my_is {
my ($a, $b, $desc) = @_;
my $res = is($a, $b, $desc);
push @results, [$cnt++, $desc, $res];
}
# For SNI/ALPN, build a protocol name as such:
# tls:sni1,sni2,...;alpn1,alpn2,...
# input: a protocol entry from Libconfig
sub make_sni_alpn_name {
my ($prot) = @_;
return "tls:" . (join ",", @{$prot->{sni_hostnames} // []})
. ";" . (join ",", @{$prot->{alpn_protocols} // [] });
}
# Tests one probe: given input data, connect, verify we get
# the expected server, verify shoveling works
# Named options:
# data: what to write
# expected: expected protocol prefix
# no_frag: don't print byte-per-byte
sub test_probe {
my (%opts) = @_;
my $cnx = new IO::Socket::INET(PeerHost => "localhost:$sslh_port");
warn "$!\n" unless $cnx;
return unless $cnx;
my $pattern = $opts{data};
if ($opts{no_frag}) {
syswrite $cnx, $pattern;
} else {
while (length $pattern) {
syswrite $cnx, (substr $pattern, 0, 1, '');
select undef, undef, undef, .01;
}
}
my $data;
my $n = sysread $cnx, $data, 1024;
$data =~ /^(.*?): /;
my $prefix = $1;
$data =~ s/$prefix: //g;
print "Received $n bytes: protocol $prefix data [$data]\n";
close $cnx;
$opts{expected} =~ s/^ssl/tls/; # to remove in 1.21
my_is($prefix, $opts{expected}, "$opts{binary}:$opts{expected}: probe connected correctly");
my_is($data, $opts{data}, "$opts{binary}:$opts{expected}: data shoveled correctly");
}
# Test all probes, with or without fragmentation
# options:
# no_frag: write test patterns all at once (also
# available per-protocol as some probes don't support
# fragmentation)
sub test_probes {
my (%in_opts) = @_;
my @probes = @{$conf->fetch_array("protocols")};
foreach my $p (@probes) {
my %protocols = (
'ssh' => { data => "SSH-2.0 tester" },
'socks5' => { data => "\x05\x04\x01\x02\x03\x04" },
'http' => {
data => "GET index.html HTTP/1.1",
no_frag => 1 },
'tls' => {
# Packet with SNI and ALPN (`openssl s_client -connect localhost:443 -alpn alpn1 -servername sni1`)
data_sni_alpn => "\x16\x03\x01\x00\xc4\x01\x00\x00\xc0\x03\x03\x03\x19\x01\x00\x40\x14\x13\xcc\x1b\x94\xad\x20\x5d\x13\x1a\x8d\xd2\x65\x23\x70\xde\xd1\x3c\x5d\x05\x19\xcb\x27\x0d\x7c\x2c\x89\x00\x00\x38\xc0\x2c\xc0\x30\x00\x9f\xcc\xa9\xcc\xa8\xcc\xaa\xc0\x2b\xc0\x2f\x00\x9e\xc0\x24\xc0\x28\x00\x6b\xc0\x23\xc0\x27\x00\x67\xc0\x0a\xc0\x14\x00\x39\xc0\x09\xc0\x13\x00\x33\x00\x9d\x00\x9c\x00\x3d\x00\x3c\x00\x35\x00\x2f\x00\xff\x01\x00\x00\x5f\x00\x00\x00\x09\x00\x07\x00\x00\x04\$sni\x00\x0b\x00\x04\x03\x00\x01\x02\x00\x0a\x00\x0a\x00\x08\x00\x1d\x00\x17\x00\x19\x00\x18\x00\x23\x00\x00\x00\x0d\x00\x20\x00\x1e\x06\x01\x06\x02\x06\x03\x05\x01\x05\x02\x05\x03\x04\x01\x04\x02\x04\x03\x03\x01\x03\x02\x03\x03\x02\x01\x02\x02\x02\x03\x00\x10\x00\x08\x00\x06\x05\$alpn\x00\x16\x00\x00\x00\x17\x00\x00hello sni/alpn",
# Packet with SNI alone
data_sni => "\x16\x03\x01\x00\xb8\x01\x00\x00\xb4\x03\x03\x97\xe4\xe9\xad\x86\xe1\x21\xfd\xc4\x5b\x27\x0e\xad\x4b\x55\xc2\x50\xe4\x1c\x86\x2f\x37\x25\xde\xe8\x9c\x59\xfc\x1b\xa9\x37\x32\x00\x00\x38\xc0\x2c\xc0\x30\x00\x9f\xcc\xa9\xcc\xa8\xcc\xaa\xc0\x2b\xc0\x2f\x00\x9e\xc0\x24\xc0\x28\x00\x6b\xc0\x23\xc0\x27\x00\x67\xc0\x0a\xc0\x14\x00\x39\xc0\x09\xc0\x13\x00\x33\x00\x9d\x00\x9c\x00\x3d\x00\x3c\x00\x35\x00\x2f\x00\xff\x01\x00\x00\x53\x00\x00\x00\x09\x00\x07\x00\x00\x04\$sni\x00\x0b\x00\x04\x03\x00\x01\x02\x00\x0a\x00\x0a\x00\x08\x00\x1d\x00\x17\x00\x19\x00\x18\x00\x23\x00\x00\x00\x0d\x00\x20\x00\x1e\x06\x01\x06\x02\x06\x03\x05\x01\x05\x02\x05\x03\x04\x01\x04\x02\x04\x03\x03\x01\x03\x02\x03\x03\x02\x01\x02\x02\x02\x03\x00\x16\x00\x00\x00\x17\x00\x00hello sni",
# packet with ALPN alone
data_alpn => "\x16\x03\x01\x00\xb7\x01\x00\x00\xb3\x03\x03\xe2\x90\xa2\x29\x03\x31\xad\x98\x44\x51\x54\x90\x5b\xd9\x51\x0e\x66\xb5\x3f\xe8\x8b\x09\xc9\xe4\x2b\x97\x24\xef\xad\x56\x06\xc9\x00\x00\x38\xc0\x2c\xc0\x30\x00\x9f\xcc\xa9\xcc\xa8\xcc\xaa\xc0\x2b\xc0\x2f\x00\x9e\xc0\x24\xc0\x28\x00\x6b\xc0\x23\xc0\x27\x00\x67\xc0\x0a\xc0\x14\x00\x39\xc0\x09\xc0\x13\x00\x33\x00\x9d\x00\x9c\x00\x3d\x00\x3c\x00\x35\x00\x2f\x00\xff\x01\x00\x00\x52\x00\x0b\x00\x04\x03\x00\x01\x02\x00\x0a\x00\x0a\x00\x08\x00\x1d\x00\x17\x00\x19\x00\x18\x00\x23\x00\x00\x00\x0d\x00\x20\x00\x1e\x06\x01\x06\x02\x06\x03\x05\x01\x05\x02\x05\x03\x04\x01\x04\x02\x04\x03\x03\x01\x03\x02\x03\x03\x02\x01\x02\x02\x02\x03\x00\x10\x00\x08\x00\x06\x05\$alpn\x00\x16\x00\x00\x00\x17\x00\x00hello alpn",
# packet with no SNI, no ALPN
data => "\x16\x03\x01\x00\xab\x01\x00\x00\xa7\x03\x03\x89\x22\x33\x95\x43\x7a\xc3\x89\x45\x51\x12\x3c\x28\x24\x1b\x6a\x78\xbf\xbe\x95\xd8\x90\x58\xd7\x65\xf7\xbb\x2d\xb2\x8d\xa0\x75\x00\x00\x38\xc0\x2c\xc0\x30\x00\x9f\xcc\xa9\xcc\xa8\xcc\xaa\xc0\x2b\xc0\x2f\x00\x9e\xc0\x24\xc0\x28\x00\x6b\xc0\x23\xc0\x27\x00\x67\xc0\x0a\xc0\x14\x00\x39\xc0\x09\xc0\x13\x00\x33\x00\x9d\x00\x9c\x00\x3d\x00\x3c\x00\x35\x00\x2f\x00\xff\x01\x00\x00\x46\x00\x0b\x00\x04\x03\x00\x01\x02\x00\x0a\x00\x0a\x00\x08\x00\x1d\x00\x17\x00\x19\x00\x18\x00\x23\x00\x00\x00\x0d\x00\x20\x00\x1e\x06\x01\x06\x02\x06\x03\x05\x01\x05\x02\x05\x03\x04\x01\x04\x02\x04\x03\x03\x01\x03\x02\x03\x03\x02\x01\x02\x02\x02\x03\x00\x16\x00\x00\x00\x17\x00\x00hello tls alone"
},
'openvpn' => { data => "\x00\x00" },
'syslog' => { data => "<42> My syslog message" },
'tinc' => { data => "0 hello" },
'xmpp' => {data => "I should get a real jabber connection initialisation here" },
'adb' => { data => "CNXN....................host:..." },
'anyprot' => {data => "hello anyprot this needs to be longer than xmpp and adb which expect about 50 characters, which I all have to write before the timeout!" },
);
my $pattern = $protocols{$p->{name}}->{data};
my %opts = %in_opts;
$opts{no_frag} = 1 if $protocols{$p->{name}}->{no_frag};
if ($p->{sni_hostnames} or $p->{alpn_protocols}) {
my $pname = make_sni_alpn_name($p);
my @sni = @{$p->{sni_hostnames} // [""] };
my @alpn = @{$p->{alpn_protocols} // [""] };
foreach my $sni ( @sni ) {
foreach my $alpn ( @alpn ) {
print "sni: $sni\nalpn: $alpn\n";
$pattern = $protocols{tls}->{
"data". ($sni ? "_sni" : "") .
($alpn ? "_alpn": "")
};
$pattern =~ s/(\$\w+)/$1/eeg;
test_probe(
data => $pattern,
expected => $pname,
%opts
);
}
}
} elsif ($p->{name} eq 'regex') {
foreach my $test (@{$p->{test_patterns}}) {
test_probe(
data => $test->{pattern},
expected => $test->{result},
%opts
);
}
} else {
test_probe(
data => $pattern,
expected => $p->{name},
%opts
);
}
}
}
# Start an echoserver for each service
foreach my $s (@{$conf->fetch_array("protocols")}) {
my $prefix = $s->{name};
$prefix =~ s/^ssl/tls/; # To remove in 1.21
if ($s->{sni_hostnames} or $s->{alpn_protocols}) {
$prefix = make_sni_alpn_name($s);
}
verbose_exec "./echosrv --listen $s->{host}:$s->{port} --prefix '$prefix: '";
}
#my @binaries = ('sslh-select', 'sslh-fork');
my @binaries = ('sslh-select');
for my $binary (@binaries) {
warn "Testing $binary\n";
# Start sslh with the right plumbing
my ($sslh_pid, $valgrind);
if (!($sslh_pid = fork)) {
my $user = (getpwuid $<)[0]; # Run under current username
my $cmd = "./$binary -v 4 -f -u $user -F test.cfg";
#$valgrind = 1;
#$cmd = "valgrind --leak-check=full $cmd";
verbose_exec $cmd;
exit 0;
}
warn "spawned $sslh_pid\n";
sleep 1; # Give everyone some time to start
sleep 5 if $valgrind; # valgrind can be heavy -- wait 5 seconds
my $test_data = "hello world\n";
my $ssl_test_data = "\x16\x03\x01\x00\xab\x01\x00\x00\xa7\x03\x03\x89\x22\x33\x95\x43\x7a\xc3\x89\x45\x51\x12\x3c\x28\x24\x1b\x6a\x78\xbf\xbe\x95\xd8\x90\x58\xd7\x65\xf7\xbb\x2d\xb2\x8d\xa0\x75\x00\x00\x38\xc0\x2c\xc0\x30\x00\x9f\xcc\xa9\xcc\xa8\xcc\xaa\xc0\x2b\xc0\x2f\x00\x9e\xc0\x24\xc0\x28\x00\x6b\xc0\x23\xc0\x27\x00\x67\xc0\x0a\xc0\x14\x00\x39\xc0\x09\xc0\x13\x00\x33\x00\x9d\x00\x9c\x00\x3d\x00\x3c\x00\x35\x00\x2f\x00\xff\x01\x00\x00\x46\x00\x0b\x00\x04\x03\x00\x01\x02\x00\x0a\x00\x0a\x00\x08\x00\x1d\x00\x17\x00\x19\x00\x18\x00\x23\x00\x00\x00\x0d\x00\x20\x00\x1e\x06\x01\x06\x02\x06\x03\x05\x01\x05\x02\x05\x03\x04\x01\x04\x02\x04\x03\x03\x01\x03\x02\x03\x03\x02\x01\x02\x02\x02\x03\x00\x16\x00\x00\x00\x17\x00\x00hello tls alone";
# Test: Shy SSH connection
if ($SSH_SHY_CNX) {
print "***Test: Shy SSH connection\n";
my $cnx_h = new IO::Socket::INET(PeerHost => "localhost:$sslh_port");
warn "$!\n" unless $cnx_h;
if (defined $cnx_h) {
sleep 13;
print $cnx_h $test_data;
my $data = <$cnx_h>;
my_is($data, "ssh: $test_data", "$binary: Shy SSH connection");
}
}
# Test: One SSL half-started then one SSH
if ($SSL_MIX_SSH) {
print "***Test: One SSL half-started then one SSH\n";
my $cnx_l = new IO::Socket::INET(PeerHost => "localhost:$sslh_port");
warn "$!\n" unless $cnx_l;
if (defined $cnx_l) {
print $cnx_l $ssl_test_data;
my $cnx_h= new IO::Socket::INET(PeerHost => "localhost:$sslh_port");
warn "$!\n" unless $cnx_h;
if (defined $cnx_h) {
sleep 3;
print $cnx_h $test_data;
my $data_h = <$cnx_h>;
my_is($data_h, "ssh: $test_data", "$binary: SSH during SSL being established");
}
my $data;
my $n = sysread $cnx_l, $data, 1024;
my_is($data, "tls: $ssl_test_data", "$binary: SSL connection interrupted by SSH");
}
}
# Test: One SSH half-started then one SSL
if ($SSH_MIX_SSL) {
print "***Test: One SSH half-started then one SSL\n";
my $cnx_h = new IO::Socket::INET(PeerHost => "localhost:$sslh_port");
warn "$!\n" unless $cnx_h;
if (defined $cnx_h) {
sleep 3;
my $cnx_l = new IO::Socket::INET(PeerHost => "localhost:$sslh_port");
warn "$!\n" unless $cnx_l;
if (defined $cnx_l) {
print $cnx_l $ssl_test_data;
my $data;
my $n = sysread $cnx_l, $data, 1024;
my_is($data, "tls: $ssl_test_data", "$binary: SSL during SSH being established");
}
print $cnx_h $test_data;
my $data = <$cnx_h>;
my_is($data, "ssh: $test_data", "$binary: SSH connection interrupted by SSL");
}
}
# Test: Drop connection without writing anything
if ($DROP_CNX) {
print "***Test: Connect but don't write anything\n";
my $cnx_h = new IO::Socket::INET(PeerHost => "localhost:$sslh_port");
warn "$!\n" unless $cnx_h;
if ($cnx_h) {
close $cnx_h;
my_is(1, "$binary: Connect and write nothing");
# The goal of the test is to check sslh doesn't
# crash
}
}
if ($PROBES_NOFRAG) {
test_probes(no_frag => 1, binary => $binary);
}
if ($PROBES_AGAIN) {
test_probes(binary => $binary);
}
my $pid = `cat $pidfile`;
warn "killing $pid\n";
kill TERM => $pid or warn "kill process: $!\n";
sleep 1;
}
# Robustness: Connecting to non-existant server
if ($RB_CNX_NOSERVER) {
print "***Test: Connecting to non-existant server\n";
my $sslh_pid;
if (!($sslh_pid = fork)) {
exec "./sslh-select -v 3 -f -u $user --listen localhost:$sslh_port --ssh localhost:$no_listen --tls localhost:$no_listen -P $pidfile";
}
warn "spawned $sslh_pid\n";
sleep 1;
my $cnx_h = new IO::Socket::INET(PeerHost => "localhost:$sslh_port");
warn "$!\n" unless $cnx_h;
if (defined $cnx_h) {
sleep 1;
my $test_data = "hello";
print $cnx_h $test_data;
}
# Ideally we should check a log is emitted.
kill TERM => `cat $pidfile` or warn "kill: $!\n";
sleep 1;
}
my $ssh_conf = (grep { $_->{name} eq "ssh" } @{$conf->fetch_array("protocols")})[0];
my $ssh_address = $ssh_conf->{host} . ":" . $ssh_conf->{port};
# Use the last TLS echoserv (no SNI/ALPN)
my $ssl_conf = (grep { $_->{name} eq "tls" } @{$conf->fetch_array("protocols")})[-1];
my $ssl_address = $ssl_conf->{host} . ":" . $ssl_conf->{port};
# Robustness: No hostname in address
if ($RB_PARAM_NOHOST) {
print "***Test: No hostname in address\n";
my $sslh_pid;
if (!($sslh_pid = fork)) {
exec "./sslh-select -v 3 -f -u $user --listen $sslh_port --ssh $ssh_address --tls $ssl_address -P $pidfile";
}
warn "spawned $sslh_pid\n";
waitpid $sslh_pid, 0;
my $code = $? >> 8;
warn "exited with $code\n";
my_is($code, 6, "Exit status on illegal option");
}
# Robustness: User does not exist
if ($RB_WRONG_USERNAME) {
print "***Test: Changing to non-existant username\n";
my $sslh_pid;
if (!($sslh_pid = fork)) {
exec "./sslh-select -v 3 -f -u ${user}_doesnt_exist --listen localhost:$no_listen --ssh $ssh_address --tls $ssl_address -P $pidfile";
}
warn "spawned $sslh_pid\n";
waitpid $sslh_pid, 0;
my $code = $? >> 8;
warn "exited with $code\n";
my_is($code, 2, "Exit status on non-existant username");
}
# Robustness: Can't open PID file
if ($RB_OPEN_PID_FILE) {
print "***Test: Can't open PID file\n";
my $sslh_pid;
if (!($sslh_pid = fork)) {
exec "./sslh-select -v 3 -f -u $user --listen localhost:$no_listen --ssh $ssh_address --tls $ssl_address -P /dont_exist/$pidfile";
# You don't have a /dont_exist/ directory, do you?!
}
warn "spawned $sslh_pid\n";
waitpid $sslh_pid, 0;
my $code = $? >> 8;
warn "exited with $code\n";
my_is($code, 3, "Exit status if can't open PID file");
}
# Robustness: Can't resolve address
if ($RB_RESOLVE_ADDRESS) {
print "***Test: Can't resolve address\n";
my $sslh_pid;
if (!($sslh_pid = fork)) {
my $user = (getpwuid $<)[0]; # Run under current username
exec "./sslh-select -v 3 -f -u $user --listen blahblah.nonexistent:9000 --ssh $ssh_address --tls $ssl_address -P $pidfile";
}
warn "spawned $sslh_pid\n";
waitpid $sslh_pid, 0;
my $code = $? >> 8;
warn "exited with $code\n";
my_is($code, 4, "Exit status if can't resolve address");
}
# Robustness: verify all command line options work
if ($RB_CL_PARAMS) {
print "***Test: Command line parameters\n";
my $sslh_pid;
if (!($sslh_pid = fork)) {
my $user = (getpwuid $<)[0]; # Run under current username
# This doesn't test --inetd
exec "./sslh-select -v 3 -f -u $user -P $pidfile".
" -n --transparent --timeout 10 -C /tmp".
" --syslog-facility auth --on-timeout ssh".
" --listen localhost:$no_listen --ssh $ssh_address --tls $ssl_address".
" --openvpn localhost:$no_listen".
" --tinc localhost:$no_listen".
" --xmpp localhost:$no_listen".
" --http localhost:$no_listen".
" --adb localhost:$no_listen".
" --socks5 localhost:$no_listen".
" --anyprot localhost:$no_listen";
exit 0;
}
warn "spawned $sslh_pid\n";
# It will die soon because $user cannot chroot (you
# don't test as root, do you?)
waitpid $sslh_pid, 0;
my $code = $? >> 8;
warn "exited with $code\n";
my_is($code, 1, "Command line arguments");
print "***Test: Bad command line parameters\n";
my $sslh_pid;
if (!($sslh_pid = fork)) {
my $user = (getpwuid $<)[0]; # Run under current username
# This doesn't test --inetd
exec "./sslh-select -v 3 -f -u $user -P $pidfile".
" -n --transparent --timeout 10 -C /tmp".
" --fakeoption".
" --anyprot localhost:$no_listen";
exit 0;
}
warn "spawned $sslh_pid\n";
waitpid $sslh_pid, 0;
my $code = $? >> 8;
warn "exited with $code\n";
my_is($code, 6, "Bad command line parameters");
}
`lcov --directory . --capture --output-file sslh_cov.info`;
`genhtml sslh_cov.info`;
`killall echosrv`;
format test_results_top =
ID | Description | Status
----+-------------------------------------------------------------------+-------
.
format test_results =
@>> | @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< | @>>
$_->[0], $_->[1], $_->[2] ? "OK" : "NOK"
.
format_name STDOUT "test_results";
format_top_name STDOUT "test_results_top";
map { write; } @results;
Executable
+278
View File
@@ -0,0 +1,278 @@
#! /usr/bin/perl -w
# Test script for sslh -- mass communication
# This creates many clients that perform concurrent
# connections, disconnect at any time, and try to generally
# behave as badly as possible.
# It can be used to test sslh behaves properly with many
# clients, however its main use is to get an idea of how
# much load it can take on your system before things start
# to go wrong.
use strict;
use IO::Socket::INET6;
use Data::Dumper;
use Conf::Libconfig 1.0.3;
## BEGIN TEST CONFIG
# How many total clients to we start? Each client will pick
# a new protocol among what's in test.cfg.
my $NUM_CNX = 16;
# Delay between starting new processes when starting up. If
# you start 200 processes in under a second, things go wrong
# and it's not sslh's fault (typically the echosrv won't be
# forking fast enough).
my $start_time_delay = .5;
# Max times we repeat the test string: allows to test for
# large messages.
my $block_rpt = 5;
# Probability to stop a client after a message (e.g. with
# .01 a client will send an average of 100 messages before
# disconnecting).
my $stop_client_probability = .0001;
##END CONFIG
my $conf = new Conf::Libconfig;
$conf->read_file("test.cfg");
# Pick one address for TCP and one for UDP
my @listen = @{$conf->value("listen")};
my ($sslh_tcp_address, $sslh_udp_address);
foreach my $l (@listen) {
if ($l->{is_udp}) {
$sslh_udp_address //= "$l->{host}:$l->{port}";
} else {
$sslh_tcp_address //= "$l->{host}:$l->{port}";
}
last if defined $sslh_tcp_address and defined $sslh_udp_address;
}
# code snippets to connect to each protocol
my %connect_params = (
regex => {
is_udp => 1,
sleep => 0,
test_data => "foo bar",
resp_len => 12,
},
ssh => {
sleep => 20, # So it times out 50% of connections
test_data => "SSH-2.0 hello",
resp_len => 18, # length "ssh: SSH-2.0 hello" => 18
},
tinc => {
sleep => 0,
test_data => "0 ",
resp_len => 8, # length "tinc: 0 " => 10
},
openvpn => {
sleep => 0,
test_data => "\x00\x00",
resp_len => 11, # length "openvpn: \x0\x0" => 11
},
);
sub connect_service {
my ($cnx, $service) = @_;
my $params = $connect_params{$service};
sleep rand $params->{sleep};
my $test_data = $params->{test_data};
syswrite $cnx, $test_data;
sleep 1;
sysread $cnx, my $r, $params->{resp_len};
my $expected = "$service: $test_data";
return ($r eq $expected);
}
sub client {
my ($protocol, $client_id, $fd_out) = @_;
my $service = $protocol->{name};
while (1) {
my $r;
#warn "$client_id: connect $sslh_tcp_address\n";
my $cnx = new IO::Socket::INET(PeerHost => $sslh_tcp_address);
die "$@\n" if (!$cnx);
my $cnt = 0;
#warn "$client_id: connecting $service\n";
if (not connect_service($cnx, $service)) {
print $fd_out "$client_id\t0\tC\n";
warn "$client_id: connecting failed\n";
exit;
}
#warn "$client_id: shoveling $service\n";
while (1) {
my $test_data = "$service $cnt" x int(rand($block_rpt)+1) . "\n";
print $cnx $test_data;
$r = <$cnx>;
my $expected= "$test_data";
my $r_l = length $r;
my $e_l = length $expected;
$fd_out->autoflush;
my $error = "";
$error = "E" if $r ne $expected;
print $fd_out ("$client_id\t$r_l\t$error\n");
($? = 1, die "$service got [$r] expected [$expected]\n") if ($r ne $expected);
if (rand(1) < $stop_client_probability) {
print $fd_out ("$client_id\t$r_l\tD\n");
last;
}
$cnt++;
}
}
exit 0;
}
# For now, a simple regex client
sub udp_client {
my ($protocol, $client_id, $fd_out) = @_;
warn "UDP client starts\n";
while (1) {
my $cnx = new IO::Socket::INET(Proto => 'udp', PeerHost => $sslh_udp_address);
# my $cnx; socket $cnx, PF_INET, SOCK_DGRAM, 0 or die "socket: $!\n";
die "$@\n" if (!$cnx);
my $cnt = 0;
while (1) {
my $test_data = "foo udp $cnt"x int(rand($block_rpt)+1). "\n";
my $ipaddr = inet_aton("localhost");
my $portaddr = sockaddr_in(8086, $ipaddr);
my $res = send($cnx, $test_data, 0, $portaddr);
if ($res != length($test_data)) {
die "cannot sendto: $!";
}
my $expected= "$protocol->{name}: $test_data";
my $r;
defined(recv($cnx, $r, length $expected, 0)) or die "recv: $!\n";
my $r_l = length $r;
my $e_l = length $expected;
$fd_out->autoflush;
my $error = "";
$error = "M" if $r ne $expected;
print $fd_out ("$client_id\t$r_l\t$error\n");
($? = 1, die "udp got [$r] expected [$expected]\n") if ($r ne $expected);
if (rand(1) < $stop_client_probability) {
print $fd_out ("$client_id\t$r_l\tD\n");
last;
}
$cnt++;
}
}
}
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";
warn "$cmd\n";
exec $cmd;
exit;
}
}
warn "Don't forget to run sslh -F test.cfg!\n";
sleep 2; # Let echosrv's and sslh start
my ($c_in, $c_out);
pipe $c_in, $c_out;
my @protocols = @{$conf->value("protocols")};
if (!fork) {
# Process that starts all the clients
for my $client_num (1 .. $NUM_CNX) {
if (!fork) {
my @supported_protocols = keys %connect_params;
my $p_name = $supported_protocols[rand @supported_protocols];
my @p = grep { $_->{name} eq $p_name } @protocols;
my $p = shift @p;
if ($p->{is_udp}) {
udp_client($p, "$p->{name}$client_num", $c_out);
} else {
client($p, "$p->{name}$client_num", $c_out);
}
exit;
}
# Give a little time so we don't overrun the
# listen(2) backlog.
select undef, undef, undef, $start_time_delay;
}
exit;
} else {
my %data;
# The condition here selects between pretty output or
# raw output
if (1) {
my $CLEAR_LINE = "\033[2K";
my $CURSOR_HOME = "\033[1;1H";
my $CLEAR_SCREEN = "\033[2J";
# Process that retrieves client output to pretty print
print $CLEAR_SCREEN; # Clear screen
while (<$c_in>) {
chop;
my ($client_id, $r_l, $error, @rest) = split /\t/, $_;
$data{$client_id} = [ 0, ""] if not exists $data{$client_id};
my ($curr_rcv) = ${$data{$client_id}}[0] + $r_l;;
$error //= "";
my ($curr_error) = "${$data{$client_id}}[1]$error";
# Consolidate into... roman numerals!
$curr_error =~ s/D{10}/X/;
$curr_error =~ s/X{10}/C/;
$curr_error =~ s/C{10}/M/;
$data{$client_id} = [ $r_l + $curr_rcv, "$curr_error$error" ];
$client_id =~ /(\d+)/;
my $i = $1;
# print $CURSOR_HOME;
print "\033[$i;1H$CLEAR_LINE$client_id\t$curr_rcv\t$curr_error\n";
#foreach my $i (sort keys %data) {
# ($r_l, $error) = @{$data{$i}};
# print "$CLEAR_LINE$i\t$r_l\t$error\n";
}
} else {
# Just print the client outputs
while (<$c_in>) {
print;
}
}
}
warn "waiting\n";
wait;
warn "finished waiting\n";
`killall echosrv`;
+333
View File
@@ -0,0 +1,333 @@
/*
tcp-listener.c: handles demultiplexing TCP protocols
# Copyright (C) 2022 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 <errno.h>
#include "tcp-listener.h"
#include "probe.h"
#include "log.h"
/* Removes cnx from probing list */
static void remove_probing_cnx(struct loop_info* fd_info, struct connection* cnx)
{
gap_remove_ptr(fd_info->probing_list, cnx, fd_info->num_probing);
fd_info->num_probing--;
}
static void add_probing_cnx(struct loop_info* fd_info, struct connection* cnx)
{
gap_set(fd_info->probing_list, fd_info->num_probing, cnx);
fd_info->num_probing++;
}
/* shovels data from active fd to the other
returns after one socket closed or operation would block
*/
static void shovel(struct connection *cnx, int active_fd, struct loop_info* fd_info)
{
struct queue *read_q, *write_q;
read_q = &cnx->q[active_fd];
write_q = &cnx->q[1-active_fd];
print_message(msg_fd, "activity on fd%d\n", read_q->fd);
switch(fd2fd(write_q, read_q)) {
case -1:
case FD_CNXCLOSED:
tidy_connection(cnx, fd_info);
break;
case FD_STALLED:
watchers_add_write(fd_info->watchers, write_q->fd);
watchers_del_read(fd_info->watchers, read_q->fd);
break;
default: /* Nothing */
break;
}
}
/* Returns the queue index that contains the specified file descriptor.
* This is called by the *_process functions, which got cnx from the fd, so it
* is impossible for fd to not be cnx, hence we die if it happens */
static int active_queue(struct connection* cnx, int fd)
{
if (cnx->q[0].fd == fd) return 0;
if (cnx->q[1].fd == fd) return 1;
print_message(msg_int_error, "file descriptor %d not found in connection object\n", fd);
exit(1);
}
/* Process a TCP read event on the specified file descriptor */
void tcp_read_process(struct loop_info* fd_info,
int fd)
{
cnx_collection* collection = fd_info->collection;
struct connection* cnx = collection_get_cnx_from_fd(collection, fd);
/* connection can get tidied if there is an error on the other file
* descriptor -- then cnx is NULL */
if (!cnx) return;
/* Determine active queue (0 or 1): if fd is that of q[1], active_q = 1,
* otherwise it's 0 */
int active_q = active_queue(cnx, fd);
switch (cnx->state) {
case ST_PROBING:
if (active_q == 1) {
print_message(msg_int_error, "Activity on fd2 while probing, impossible\n");
dump_connection(cnx);
exit(1);
}
probing_read_process(cnx, fd_info);
break;
case ST_SHOVELING:
shovel(cnx, active_q, fd_info);
break;
default: /* illegal */
print_message(msg_int_error, "Illegal connection state %d\n", cnx->state);
dump_connection(cnx);
exit(1);
}
}
/* Accepts a connection from the main socket and assigns it to an empty slot.
* If no slots are available, allocate another few. If that fails, drop the
* connexion */
struct connection* accept_new_connection(int listen_socket, struct loop_info* fd_info)
{
int in_socket, res;
print_message(msg_fd, "accepting from %d\n", listen_socket);
in_socket = accept(listen_socket, 0, 0);
CHECK_RES_RETURN(in_socket, "accept", NULL);
res = set_nonblock(in_socket);
if (res == -1) {
close(in_socket);
return NULL;
}
struct connection* cnx = collection_alloc_cnx_from_fd(fd_info->collection, in_socket);
if (!cnx) {
close(in_socket);
return NULL;
}
add_probing_cnx(fd_info, cnx);
return cnx;
}
/* Connect queue 1 of connection to SSL; returns new file descriptor */
static int connect_queue(struct connection* cnx,
struct loop_info* fd_info)
{
struct queue *q = &cnx->q[1];
q->fd = connect_addr(cnx, cnx->q[0].fd, NON_BLOCKING);
if (q->fd != -1) {
log_connection(NULL, cnx);
flush_deferred(q);
if (q->deferred_data) {
watchers_add_write(fd_info->watchers, q->fd);
watchers_del_read(fd_info->watchers, cnx->q[0].fd);
}
watchers_add_read(fd_info->watchers, q->fd);
collection_add_fd(fd_info->collection, cnx, q->fd);
return q->fd;
} else {
tidy_connection(cnx, fd_info);
return -1;
}
}
/* shovels data from one fd to the other and vice-versa
returns after one socket closed
*/
static void shovel_single(struct connection *cnx)
{
fd_set fds_r, fds_w;
int res, i;
int max_fd = MAX(cnx->q[0].fd, cnx->q[1].fd) + 1;
FD_ZERO(&fds_r);
FD_ZERO(&fds_w);
while (1) {
for (i = 0; i < 2; i++) {
if (cnx->q[i].deferred_data_size) {
FD_SET(cnx->q[i].fd, &fds_w);
FD_CLR(cnx->q[1-i].fd, &fds_r);
} else {
FD_CLR(cnx->q[i].fd, &fds_w);
FD_SET(cnx->q[1-i].fd, &fds_r);
}
}
res = select(
max_fd,
&fds_r,
&fds_w,
NULL,
NULL
);
CHECK_RES_DIE(res, "select");
for (i = 0; i < 2; i++) {
if (FD_ISSET(cnx->q[i].fd, &fds_w)) {
res = flush_deferred(&cnx->q[i]);
if ((res == -1) && ((errno == EPIPE) || (errno == ECONNRESET))) {
print_message(msg_fd, "%s socket closed\n", i ? "server" : "client");
return;
}
}
if (FD_ISSET(cnx->q[i].fd, &fds_r)) {
res = fd2fd(&cnx->q[1-i], &cnx->q[i]);
if (!res) {
print_message(msg_fd, "socket closed\n");
return;
}
}
}
}
}
/* Child process that makes internal connection and proxies
*/
static void connect_proxy(struct connection *cnx)
{
int in_socket;
int out_socket;
/* Minimize the file descriptor value to help select() */
in_socket = dup(cnx->q[0].fd);
if (in_socket == -1) {
in_socket = cnx->q[0].fd;
} else {
close(cnx->q[0].fd);
cnx->q[0].fd = in_socket;
}
/* Connect the target socket */
out_socket = connect_addr(cnx, in_socket, BLOCKING);
CHECK_RES_DIE(out_socket, "connect");
cnx->q[1].fd = out_socket;
log_connection(NULL, cnx);
shovel_single(cnx);
close(in_socket);
close(out_socket);
print_message(msg_fd, "connection closed down\n");
exit(0);
}
/* Process read activity on a socket in probe state
* IN/OUT cnx: connection data, updated if connected
* IN/OUT info: updated if connected
* */
void probing_read_process(struct connection* cnx,
struct loop_info* fd_info)
{
int res;
/* If timed out it's SSH, otherwise the client sent
* data so probe the protocol */
if ((cnx->probe_timeout < time(NULL))) {
cnx->proto = timeout_protocol();
print_message(msg_fd, "timed out, connect to %s\n", cnx->proto->name);
} else {
res = probe_client_protocol(cnx);
if (res == PROBE_AGAIN)
return;
}
remove_probing_cnx(fd_info, cnx);
cnx->state = ST_SHOVELING;
/* libwrap check if required for this protocol */
if (cnx->proto->service &&
check_access_rights(cnx->q[0].fd, cnx->proto->service)) {
tidy_connection(cnx, fd_info);
res = -1;
} else if (cnx->proto->fork) {
switch (fork()) {
case 0: /* child */
/* TODO: close all file descriptors except 2 */
/* free(cnx); */
connect_proxy(cnx);
exit(0);
case -1: print_message(msg_system_error, "fork failed: err %d: %s\n", errno, strerror(errno));
break;
default: /* parent */
break;
}
tidy_connection(cnx, fd_info);
res = -1;
} else {
res = connect_queue(cnx, fd_info);
}
}
/* Process a connection that is active in write (this is TCP only, as
* UDP sockets are never "full" or deferred or whatever) */
void cnx_write_process(struct loop_info* fd_info, int fd)
{
struct connection* cnx = collection_get_cnx_from_fd(fd_info->collection, fd);
/* connection can get tidied if there is an error on the other file
* descriptor -- then cnx is NULL */
if (!cnx) return;
int res;
int queue = active_queue(cnx, fd);
res = flush_deferred(&cnx->q[queue]);
if ((res == -1) && ((errno == EPIPE) || (errno == ECONNRESET))) {
if (cnx->state == ST_PROBING) remove_probing_cnx(fd_info, cnx);
tidy_connection(cnx, fd_info);
} else {
/* If no deferred data is left, stop monitoring the fd
* for write, and restart monitoring the other one for reads*/
if (!cnx->q[queue].deferred_data_size) {
watchers_del_write(fd_info->watchers, cnx->q[queue].fd);
watchers_add_read(fd_info->watchers, cnx->q[1-queue].fd);
}
}
}
+13
View File
@@ -0,0 +1,13 @@
#ifndef TCP_LISTENER_H
#define TCP_LISTENER_H
#include "processes.h"
#include "collection.h"
#include "tcp-probe.h"
void tcp_read_process(struct loop_info* fd_info, int fd);
struct connection* accept_new_connection(int listen_socket, struct loop_info* fd_info);
void probing_read_process(struct connection* cnx, struct loop_info* fd_info);
void cnx_write_process(struct loop_info* fd_info, int fd);
#endif
+99
View File
@@ -0,0 +1,99 @@
/*
# tcp-probe.c: TCP code that is common to the sslh-fork and sslh-[ev|select]
#
# Copyright (C) 2022 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 "probe.h"
static struct sslhcfg_protocols_item** tcp_protocols;
static int tcp_protocols_len = 0;
/*
* Read the beginning of data coming from the client connection and check if
* it's a known protocol.
* Return PROBE_AGAIN if not enough data, or PROBE_MATCH if it succeeded in
* which case cnx->proto is set to the appropriate protocol.
*/
int probe_client_protocol(struct connection *cnx)
{
char buffer[BUFSIZ];
ssize_t n;
n = read(cnx->q[0].fd, buffer, sizeof(buffer));
/* It's possible that read() returns an error, e.g. if the client
* disconnected between the previous call to select() and now. If that
* happens, we just connect to the default protocol so the caller of this
* function does not have to deal with a specific failure condition (the
* connection will just fail later normally). */
if (n > 0) {
defer_write(&cnx->q[1], buffer, n);
return probe_buffer(cnx->q[1].begin_deferred_data,
cnx->q[1].deferred_data_size,
tcp_protocols, tcp_protocols_len,
&cnx->proto
);
}
/* read() returned an error, so just connect to the last protocol to die */
cnx->proto = &cfg.protocols[cfg.protocols_len-1];
return PROBE_MATCH;
}
static void tcp_protocol_list_init(void)
{
for (int i = 0; i < cfg.protocols_len; i++) {
struct sslhcfg_protocols_item* p = &cfg.protocols[i];
if (!p->is_udp) {
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;
}
}
}
/* Configuration sanity check for TCP:
* - If there is a listening socket, there must be at least one target
*/
static void tcp_sanity_check(void)
{
int tcp_present = 0;
for (int i = 0; i < cfg.listen_len; i++) {
struct sslhcfg_listen_item* p = &cfg.listen[i];
if (!p->is_udp) {
tcp_present = 1;
break;
}
}
if (tcp_present && !tcp_protocols_len) {
print_message(msg_config_error, "At least one TCP target protocol must be specified.\n");
exit(2);
}
}
void tcp_init(void)
{
tcp_protocol_list_init();
tcp_sanity_check();
}
Loaded 100 of 107 files, more files were not shown because too many files have changed in this diff. Show more