diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml new file mode 100644 index 0000000..c7f078a --- /dev/null +++ b/.github/workflows/windows.yml @@ -0,0 +1,56 @@ +name: Build for Windows + +on: + push: + branches: + - '**' + paths-ignore: + - 'README.md' + - 'LICENSE.txt' + - 'RELNOTES' + - 'TODO' + pull_request: + release: + types: [published] + +jobs: + build: + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + - uses: msys2/setup-msys2@v2 + with: + msystem: msys + install: >- + mingw-w64-x86_64-toolchain + gcc + make + openssl + openssl-devel + zip + unzip + xmlto + asciidoc + curl + awk + bash + - name: Build + shell: msys2 {0} + run: | + make + make docs + ldd proxytunnel.exe | grep msys.*\.dll | awk '{print $3}' | xargs cp -t . + zip proxytunnel.zip proxytunnel.exe *.dll docs/proxytunnel.1 docs/*.html + - name: Upload CI Artifact + uses: actions/upload-artifact@v4 + with: + name: proxytunnel-${{ github.sha }}-x86_64-windows-msys + path: proxytunnel.zip + - name: Upload to GitHub Release + uses: svenstaro/upload-release-action@v2 + if: github.event_name == 'release' + with: + file: proxytunnel.zip + asset_name: proxytunnel-${{ github.ref_name }}-x86_64-windows-msys.zip + tag: ${{ github.ref }} + overwrite: true diff --git a/.gitignore b/.gitignore index c15583a..094661f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,7 @@ *.exec proxytunnel.exe proxytunnel +passfile +test.sh +docs/*.html +docs/*.1 diff --git a/CHANGES b/CHANGES old mode 100755 new mode 100644 index 38321b2..24d5bfb --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,85 @@ +Changes to proxytunnel 1.12.3 -- Fri Mar 7 23:04:25 CET 2025 + +- PR #83 from https://github.com/tofurky to avoid printing unterminated string + in readline(). +- PR #86 from https://github.com/e9hack to fix and improve ntlm authentication. +- PR #89 from https://github.com/njbraun to increase MAX_HEADER_SIZE to 4k. +- From Sven Geuer, https://github.com/68420948 + - Chmode 755 to 644 for file CHANGES. + - Drop obsolete entry about SSL proxy support from file TODO. + +Changes to proxytunnel 1.12.2 -- Mon Mar 25 14:50:38 CET 2024 + +- PRs #79 and #80 from https://github.com/hoilc implementing github action to + build windows binary. + +Changes to proxytunnel 1.12.1 -- Tue Feb 6 17:36:38 CET 2024 + +[ Sven Geuer, https://github.com/68420948 ] +- -a/--standalone option: + - Use an AF_INET socket when binding to a specified IPv4 address. This makes + sure IPv4 works regardless of the IPV6_V6ONLY socket option being turned on + or off. Thanks to https://github.com/saper for noting the shortcoming. + - Fix logging of IPv6 clients. + - Close unneeded listening socket in worker. +- Deprecate -L/--tlsenforce and -T/--no-ssl3. SSLv3 has been disabled in likely + all distributions nowadays. +- Apply OPENSSL_VERSION_NUMBER to compile code matching the libssl version in + use. Consequently the file Makefile.ssl11 has been removed. +- Make sure no deprecated libssl functions are called, depending on the libssl + version in use. +- Replace calls to deprecated functions bzero()/bcopy() by memset()/memcpy(). +- Update README.md to show recent --help output. + +Changes to proxytunnel 1.12.0 -- Sun Dec 17 19:51:57 CET 2023 + +[ Sven Geuer, https://github.com/68420948 ] +- New: Support authentication by SSL client certificate on SSL encrypted + tunnels, thanks to https://github.com/yayo for providing an initial patch + with issue #76, closes also issue #51. +- New: Listen also for IPv6 connections in standalone mode. +- New: Extend -a/--standalone option to allow for binding to a specified IPv4 + or IPv6 address, thanks to https://github.com/saper for providing an initial + implementation with PR #77. +- Honor -o/--host on determining the SNI host name. +- Fix loading REMPROXYUSER/REMPROXYPASS from the environment. +- Update manual page, correct errors, fix typos. +- Minor corrections to README.md + +Changes to proxytunnel 1.11.1 -- Mon Oct 16 20:01:04 CEST 2023 + +[ Sven Geuer, https://github.com/68420948 ] +- Remediate the faulty patch for issue #57, thanks to https://github.com/e9hack + and https://github.com/yurivict for raising issues #59 and #69 +- Fix NTLM based authentication on 64bit machines, thanks to + https://github.com/e9hack for raising issue #60 +- Harmonize output of option --help and content of the manual page +- Correct formatting errors and typos in the manual page +- Make config.c central for setting version related information in the manual + page and the application +- Return to version number format major.minor.patch + +Changes to proxytunnel 1.11 -- Sun Sep 3 12:04:27 AM CEST 2023 + +- Patch from https://github.com/68420948 to add -4 and -6 options + +Changes to proxytunnel 1.10.20220528 -- Sat 28 May 2022 03:54:20 PM CEST + +- Patch from https://github.com/ZjYwMj fixes + https://github.com/proxytunnel/proxytunnel/issues/57 + +Changes to proxytunnel 1.10.20210609 -- Wed Jun 9 11:55:54 CEST 2021 + +- No functional changes +- Builds have been migrated to travis-ci.com (from .org) + +Changes to proxytunnel 1.10.20210128 -- Thu 28 Jan 2021 10:23:24 PM CET + +- Changed version to 1.10.20210128 +- Applied 2 more debian patches by Julian Gilbey +- Error handling on SSL_new / SSL_connect +- Allow for longer username/passwords fields (was 24 chars) + Changes to proxytunnel 1.10.20200507 -- Thu 07 May 2020 05:13:01 PM CEST - Applied 3 patches from debian's package diff --git a/INSTALL.md b/INSTALL.md index 9e2e7fa..a7db3fe 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -10,6 +10,55 @@ to build simply run `make` and optionally `make install`. If you manually want to install, copy proxytunnel to /usr/local/bin and optionally the manual-page from the debian-subdirectory to your manpath +# Nix Flakes + +> NOTE: The Nix Flake installation currently only supports the `x86_64-linux` platform, and has not been tested on other architectures. + +A simple Nix Flake is included to allow for use via flake inputs. To create a temporary Nix Shell with access to the `proxytunnel` binary, you can run the command: +```console +nix develop github:proxytunnel/proxytunnel +``` +If you instead want to include it as a flake input, the following `flake.nix` shows how to do so: +```nix +{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + + # Add proxytunnel as an input + proxytunnel.url = "github:proxytunnel/proxytunnel"; + }; + + outputs = { + nixpkgs, + proxytunnel, + ... + }: let + system = "x86_64-linux"; + pkgs = import nixpkgs { + system = "x86_64-linux"; + overlays = [ + # Add proxytunnel's default features to your nixpkgs + proxytunnel = proxytunnel.overlays.default; + + # For a full list of override options, see `nix/proxytunnel.nix` + ]; + }; + in { + devShells.${system}.default = pkgs.mkShell { + packages = [ + # Make the `proxytunnel` binary available in a Nix Shell + # The above overlay adds it to nixpkgs. Without the overlay, use proxytunnel.packages.${system}.default + pkgs.proxytunnel + + # And include any other packages as desired... + pkgs.gcc + # ... + ]; + }; + }; +} +``` + # msys2 To install msys2 with [chocolatey](https://chocolatey.org/install): diff --git a/Makefile b/Makefile index ecdaa5f..a925a89 100644 --- a/Makefile +++ b/Makefile @@ -14,11 +14,15 @@ OPTFLAGS += -DHAVE_GETOPT_LONG # Comment if you don't have/want ssl OPTFLAGS += -DUSE_SSL -# Most systems +# MSYS +# The current version of gcc from MSYS defines __MSYS__ and __CYGWIN__. +# To avoid to change the code, simply define CYGWIN additionally. +ifneq ($(filter $(MSYSTEM),MSYS MINGW32 MINGW64 UCRT64),) +CFLAGS += -DCYGWIN +else +# Most systems, MSYS definitely not OPTFLAGS += -DSETPROCTITLE -DSPT_TYPE=2 - -# Comment if you don't have this flag -OPTFLAGS += -DSO_REUSEPORT +endif # System dependant blocks... if your system is listed below, uncomment # the relevant lines @@ -29,6 +33,12 @@ OPTFLAGS += -DSO_REUSEPORT # DARWIN #OPTFLAGS += -DDARWIN +# DARWIN, continued, if compiling for macOS with Homebrew +#CFLAGS += -I/usr/local/opt/openssl/include +#LDFLAGS += -L/usr/local/opt/openssl/lib +#OPTFLAGS += -DDEFAULT_CA_FILE='"/usr/local/etc/openssl@1.1/cacert.pem"' +#OPTFLAGS += -DDEFAULT_CA_DIR=NULL + # CYGWIN #OPTFLAGS += -DCYGWIN @@ -56,7 +66,6 @@ mandir = $(datadir)/man OBJ = proxytunnel.o \ base64.o \ strzcat.o \ - setproctitle.o \ io.o \ http.o \ basicauth.o \ @@ -67,6 +76,10 @@ OBJ = proxytunnel.o \ ntlm.o \ ptstream.o +ifneq (,$(findstring -DSETPROCTITLE,$(OPTFLAGS))) +OBJ += setproctitle.o +endif + UNAME = $(shell uname) ifneq ($(UNAME),Darwin) OBJ += strlcpy.o \ @@ -81,7 +94,7 @@ docs: $(MAKE) -C docs proxytunnel: $(OBJ) - $(CC) -o $(name) $(CFLAGS) $(OPTFLAGS) $(OBJ) $(LDFLAGS) + $(CC) -o $(name) $(CPPFLAGS) $(CFLAGS) $(OPTFLAGS) $(OBJ) $(LDFLAGS) clean: @rm -f $(name) $(OBJ) @@ -93,7 +106,7 @@ install: $(MAKE) -C docs install .c.o: - $(CC) $(CFLAGS) $(OPTFLAGS) -c -o $@ $< + $(CC) $(CPPFLAGS) $(CFLAGS) $(OPTFLAGS) -c -o $@ $< dist: clean docs sed -i -e 's/^Version:.*$$/Version: $(version)/' contrib/proxytunnel.spec diff --git a/Makefile.ssl10 b/Makefile.ssl10 deleted file mode 100644 index 9f931a2..0000000 --- a/Makefile.ssl10 +++ /dev/null @@ -1,106 +0,0 @@ -# Makefile for proxytunnel -# -# Please uncomment the appropriate settings - -name = proxytunnel -version = $(shell awk 'BEGIN { FS="\"" } /^\#define VERSION / { print $$2 }' config.h) - -CC ?= cc -CFLAGS ?= -Wall -O2 -ggdb -DOPENSSL10 - -# Comment on non-gnu systems -OPTFLAGS += -DHAVE_GETOPT_LONG - -# Comment if you don't have/want ssl -OPTFLAGS += -DUSE_SSL - -# Most systems -OPTFLAGS += -DSETPROCTITLE -DSPT_TYPE=2 - -# Comment if you don't have this flag -OPTFLAGS += -DSO_REUSEPORT - -# System dependant blocks... if your system is listed below, uncomment -# the relevant lines - -# OpenBSD -#OPTFLAGS += -DHAVE_SYS_PSTAT_H - -# DARWIN -#OPTFLAGS += -DDARWIN - -# CYGWIN -#OPTFLAGS += -DCYGWIN - -# SOLARIS -#LDFLAGS += -lsocket -lnsl -#LDFLAGS += -L/usr/local/ssl/lib # Path to your SSL lib dir - -# END system dependant block - -SSL_LIBS := $(shell pkg-config --libs openssl 2>/dev/null) -ifeq ($(SSL_LIBS),) -SSL_LIBS := $(shell pkg-config --libs libssl 2>/dev/null) -endif -ifeq ($(SSL_LIBS),) -SSL_LIBS := -lssl -lcrypto -endif -LDFLAGS += $(SSL_LIBS) - -prefix = /usr/local -bindir = $(prefix)/bin -datadir = $(prefix)/share -mandir = $(datadir)/man - -# Remove strlcpy/strlcat on (open)bsd/darwin systems -OBJ = proxytunnel.o \ - base64.o \ - strzcat.o \ - setproctitle.o \ - io.o \ - http.o \ - basicauth.o \ - globals.o \ - readpassphrase.o \ - messages.o \ - cmdline.o \ - ntlm.o \ - ptstream.o - -UNAME = $(shell uname) -ifneq ($(UNAME),Darwin) -OBJ += strlcpy.o \ - strlcat.o -endif - -.PHONY: all clean docs install - -all: proxytunnel - -docs: - $(MAKE) -C docs - -proxytunnel: $(OBJ) - $(CC) -o $(name) $(CFLAGS) $(OPTFLAGS) $(OBJ) $(LDFLAGS) - -clean: - @rm -f $(name) $(OBJ) - $(MAKE) -C docs clean - -install: - install -d $(DESTDIR)$(bindir) - install -p -m555 $(name) $(DESTDIR)$(bindir) - $(MAKE) -C docs install - -.c.o: - $(CC) $(CFLAGS) $(OPTFLAGS) -c -o $@ $< - -dist: clean docs - sed -i -e 's/^Version:.*$$/Version: $(version)/' contrib/proxytunnel.spec - find . ! -wholename '*/.svn*' | pax -d -w -x ustar -s ,^./,$(name)-$(version)/, | bzip2 >../$(name)-$(version).tar.bz2 - -rpm: dist - rpmbuild -tb --clean --rmsource --rmspec --define "_rpmfilename %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm" --define "_rpmdir ../" ../$(name)-$(version).tar.bz2 - -srpm: dist - rpmbuild -ts --clean --rmsource --rmspec --define "_rpmfilename %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm" --define "_srcrpmdir ../" ../$(name)-$(version).tar.bz2 diff --git a/Makefile.ssl11 b/Makefile.ssl11 deleted file mode 100644 index 709b05f..0000000 --- a/Makefile.ssl11 +++ /dev/null @@ -1,106 +0,0 @@ -# Makefile for proxytunnel -# -# Please uncomment the appropriate settings - -name = proxytunnel -version = $(shell awk 'BEGIN { FS="\"" } /^\#define VERSION / { print $$2 }' config.h) - -CC ?= cc -CFLAGS ?= -Wall -O2 -ggdb -DOPENSSL11 - -# Comment on non-gnu systems -OPTFLAGS += -DHAVE_GETOPT_LONG - -# Comment if you don't have/want ssl -OPTFLAGS += -DUSE_SSL - -# Most systems -OPTFLAGS += -DSETPROCTITLE -DSPT_TYPE=2 - -# Comment if you don't have this flag -OPTFLAGS += -DSO_REUSEPORT - -# System dependant blocks... if your system is listed below, uncomment -# the relevant lines - -# OpenBSD -#OPTFLAGS += -DHAVE_SYS_PSTAT_H - -# DARWIN -#OPTFLAGS += -DDARWIN - -# CYGWIN -#OPTFLAGS += -DCYGWIN - -# SOLARIS -#LDFLAGS += -lsocket -lnsl -#LDFLAGS += -L/usr/local/ssl/lib # Path to your SSL lib dir - -# END system dependant block - -SSL_LIBS := $(shell pkg-config --libs openssl 2>/dev/null) -ifeq ($(SSL_LIBS),) -SSL_LIBS := $(shell pkg-config --libs libssl 2>/dev/null) -endif -ifeq ($(SSL_LIBS),) -SSL_LIBS := -lssl -lcrypto -endif -LDFLAGS += $(SSL_LIBS) - -prefix = /usr/local -bindir = $(prefix)/bin -datadir = $(prefix)/share -mandir = $(datadir)/man - -# Remove strlcpy/strlcat on (open)bsd/darwin systems -OBJ = proxytunnel.o \ - base64.o \ - strzcat.o \ - setproctitle.o \ - io.o \ - http.o \ - basicauth.o \ - globals.o \ - readpassphrase.o \ - messages.o \ - cmdline.o \ - ntlm.o \ - ptstream.o - -UNAME = $(shell uname) -ifneq ($(UNAME),Darwin) -OBJ += strlcpy.o \ - strlcat.o -endif - -.PHONY: all clean docs install - -all: proxytunnel - -docs: - $(MAKE) -C docs - -proxytunnel: $(OBJ) - $(CC) -o $(name) $(CFLAGS) $(OPTFLAGS) $(OBJ) $(LDFLAGS) - -clean: - @rm -f $(name) $(OBJ) - $(MAKE) -C docs clean - -install: - install -d $(DESTDIR)$(bindir) - install -p -m555 $(name) $(DESTDIR)$(bindir) - $(MAKE) -C docs install - -.c.o: - $(CC) $(CFLAGS) $(OPTFLAGS) -c -o $@ $< - -dist: clean docs - sed -i -e 's/^Version:.*$$/Version: $(version)/' contrib/proxytunnel.spec - find . ! -wholename '*/.svn*' | pax -d -w -x ustar -s ,^./,$(name)-$(version)/, | bzip2 >../$(name)-$(version).tar.bz2 - -rpm: dist - rpmbuild -tb --clean --rmsource --rmspec --define "_rpmfilename %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm" --define "_rpmdir ../" ../$(name)-$(version).tar.bz2 - -srpm: dist - rpmbuild -ts --clean --rmsource --rmspec --define "_rpmfilename %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm" --define "_srcrpmdir ../" ../$(name)-$(version).tar.bz2 diff --git a/README.md b/README.md index c11fe86..4023b95 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ [![Build Status](https://travis-ci.org/proxytunnel/proxytunnel.svg?branch=master)](https://travis-ci.org/proxytunnel/proxytunnel) +[![Maintainers Wanted](https://img.shields.io/badge/maintainers-wanted-red.svg)](https://github.com/pickhardt/maintainers-wanted) + # Proxytunnel @@ -23,37 +25,40 @@ option it specifies it's command-line options. ``` $ ./proxytunnel --help -proxytunnel 1.9.9 Copyright 2001-2018 Proxytunnel Project +proxytunnel 1.12.1 Copyright 2001-2024 Proxytunnel Project Usage: proxytunnel [OPTIONS]... Build generic tunnels through HTTPS proxies using HTTP authentication Standard options: -i, --inetd Run from inetd (default: off) - -a, --standalone=INT Run as standalone daemon on specified port + -a, --standalone=STRING Run as standalone daemon on specified port or + address:port combination -p, --proxy=STRING Local proxy host:port combination -r, --remproxy=STRING Remote proxy host:port combination (using 2 proxies) -d, --dest=STRING Destination host:port combination -e, --encrypt SSL encrypt data between local proxy and destination -E, --encrypt-proxy SSL encrypt data between client and local proxy -X, --encrypt-remproxy SSL encrypt data between local and remote proxy - -W, --wa-bug-29744 workaround ASF Bugzilla 29744, if SSL is active stop - using it after CONNECT (might not work on all setups; - see /usr/share/doc/proxytunnel/README.Debian.gz) - -B, --buggy-encrypt-proxy Equivalent to -E -W, provided for backwards - compatibility - -L (legacy) enforce TLSv1 connection - -T, --no-ssl3 Do not connect using SSLv3 Additional options for specific features: + -W, --wa-bug-29744 Workaround ASF Bugzilla 29744: if SSL is active + stop using it after CONNECT (might not work on all + setups) + -B, --buggy-encrypt-proxy Equivalent to -E -W, provided for backwards + compatibility -z, --no-check-certificate Don't verify server SSL certificate -C, --cacert=STRING Path to trusted CA certificate or directory + -4, --ipv4 Enforce IPv4 connection to local proxy + -6, --ipv6 Enforce IPv6 connection to local proxy -F, --passfile=STRING File with credentials for proxy authentication -P, --proxyauth=STRING Proxy auth credentials user:pass combination -R, --remproxyauth=STRING Remote proxy auth credentials user:pass combination + -c, --cert=FILENAME client SSL certificate (chain) + -k, --key=FILENAME client SSL key -N, --ntlm Use NTLM based authentication -t, --domain=STRING NTLM domain (default: autodetect) -H, --header=STRING Add additional HTTP headers to send to proxy - -o STRING send custom Host Header + -o, --host=STRING Send custom Host Header/SNI -x, --proctitle=STRING Use a different process title Miscellaneous options: @@ -69,14 +74,14 @@ a $HOME/.ssh/config file with the following content: ``` Host foobar ProtocolKeepAlives 30 - ProxyCommand /path/to/proxytunnel -p proxy:8080 -P username --d mybox.athome.nl:443 + ProxyCommand /path/to/proxytunnel -E -p proxy:8080 -P username -d mybox.athome.nl:443 ``` With: ``` - foobar The symbolic name of the host you want to connect to +- -E Option to use encryption to communicate to the proxy (use https) - proxy The host name of the proxy you want to connect through - 8080 The port number where the proxy software listens to - username Your proxy userid (password will be prompted) @@ -84,12 +89,16 @@ With: - 443 The port number of the SSH daemon on mybox.athome.nl ``` +Optional arguments: +``` +- -z Don't verify server SSL certificate (for example in case of self-signed certificate) +``` + If your proxy doesn't require the username and password for using it, you can skip these options. If you don't provide the password on the command-line (which is recommended) you will be prompted for it by proxytunnel. If you are on a trusted system you can also put the -password in an environment variable, and tell proxytunnel where to -find it with '-S'. +password in the environment variable PROXYPASS. If you want to run proxytunnel from inetd add the '--inetd' option. @@ -107,8 +116,7 @@ auto-detection doesn't work for you (which is usually doesn't) If you want to have the first proxy connect to another http proxy (like one you can control, specify -r proxy2:port. The first proxy will then connect to this remote proxy, which will be asked to connect to the -requested destination. Note that authentication doesn't (yet) work on -this remote proxy. For more information regarding this feature, check +requested destination. For more information regarding this feature, check out http://dag.wieers.com/howto/ssh-http-tunneling/ If your proxy is more advanced, and does protocol inspection it will diff --git a/TODO b/TODO index 45c19e4..8ac9281 100644 --- a/TODO +++ b/TODO @@ -7,11 +7,6 @@ or: proxytunnel -p username:password@local-proxy:port -r username:password@remote-proxy:port -d %h:%p -### SSL proxy support -- Starting with Apache 2.4 using CONNECT over SSL is supported !! - See: http://issues.apache.org/bugzilla/show_bug.cgi?id=29744 - - ### Code cleanup - Find some hardcore C experts to help us improve the code quality diff --git a/basicauth.c b/basicauth.c index ea2b925..cc857e2 100644 --- a/basicauth.c +++ b/basicauth.c @@ -32,10 +32,10 @@ * is stored in basicauth. */ char *basicauth(char *user, char *pass) { - char *b64str = malloc(80); + char *b64str = malloc(160); int len = strlen( user ) + strlen( pass ) + 2; - char *p = (char *) malloc( len ); + char *p = (char *) alloca( len ); /* Set up the cookie in clear text */ sprintf( p, "%s:%s", user, pass ); @@ -50,8 +50,6 @@ char *basicauth(char *user, char *pass) { // message( "Proxy basic auth of %s is %s\n", p, basicauth ); // } - free( p ); - return b64str; } diff --git a/buildwin.sh b/buildwin.sh index 52d8b59..5e1165c 100644 --- a/buildwin.sh +++ b/buildwin.sh @@ -4,13 +4,13 @@ echo "Build docs..." make -C docs echo "Build proxytunnel..." -make -f Makefile.ssl11 - -echo "Copy msys/openssl dll to build dir..." -cp /usr/bin/msys-2.0.dll /usr/bin/msys-crypto-1.1.dll /usr/bin/msys-ssl-1.1.dll /usr/bin/msys-z.dll . +make -f Makefile +strip -s proxytunnel.exe echo "Generate proxytunnel.zip with docs, exe and msys/openssl dll..." -zip proxytunnel.zip proxytunnel.exe *.dll docs/proxytunnel.1 docs/proxytunnel.1.html docs/proxytunnel-paper.html +zip proxytunnel.zip proxytunnel.exe docs/proxytunnel.1 docs/proxytunnel.1.html docs/proxytunnel-paper.html +DLLS="$(ldd proxytunnel.exe | grep msys.*\.dll | awk '{print $3}' | xargs) /usr/lib/ossl-modules/legacy.dll" +zip proxytunnel.zip -j $DLLS if [ ! -z "${TRAVIS_TAG}" ]; then echo "Deploy proxytunnel.zip to github release tag:${TRAVIS_TAG}..." diff --git a/cmdline.c b/cmdline.c index 7ccadee..7e5db30 100644 --- a/cmdline.c +++ b/cmdline.c @@ -38,7 +38,7 @@ extern char * optarg; static char *getCredentialsFromFile( const char* filename, char **user, char **pass, char **rem_user, char **rem_pass); void cmdline_parser_print_version (void) { - printf ("%s %s Copyright 2001-2020 Proxytunnel Project\n", PACKAGE, VERSION); + printf ("%s %s Copyright 2001-%s Proxytunnel Project\n", PACKAGE, VERSION, VERSION_YEAR); } void cmdline_parser_print_help (void) { @@ -50,8 +50,9 @@ void cmdline_parser_print_help (void) { "Standard options:\n" // FIXME: " -c, --config=FILE Read config options from file\n" " -i, --inetd Run from inetd (default: off)\n" -" -a, --standalone=INT Run as standalone daemon on specified port\n" -// FIXME: " -f, --nobackground Don't for tok background in standalone mode\n" +" -a, --standalone=STRING Run as standalone daemon on specified port or\n" +" address:port combination\n" +// FIXME: " -f, --nobackground Don't fork to background in standalone mode\n" " -p, --proxy=STRING Local proxy host:port combination\n" " -r, --remproxy=STRING Remote proxy host:port combination (using 2 proxies)\n" " -d, --dest=STRING Destination host:port combination\n" @@ -59,23 +60,29 @@ void cmdline_parser_print_help (void) { " -e, --encrypt SSL encrypt data between local proxy and destination\n" " -E, --encrypt-proxy SSL encrypt data between client and local proxy\n" " -X, --encrypt-remproxy SSL encrypt data between local and remote proxy\n" -" -W, --wa-bug-29744 Workaround ASF Bugzilla 29744: if SSL is active stop\n" -" using it after CONNECT (might not work on all setups; see\n" -" /usr/share/doc/proxytunnel/README.Debian.gz)\n" -" -B, --buggy-encrypt-proxy Equivalent to -E -W, provided for backwards\n" -" compatibility\n" -" -L (legacy) enforce TLSv1 connection\n" -" -T, --no-ssl3 Do not connect using SSLv3\n" #endif "\n" "Additional options for specific features:\n" #ifdef USE_SSL +" -W, --wa-bug-29744 Workaround ASF Bugzilla 29744: if SSL is active\n" +" stop using it after CONNECT (might not work on all\n" +" setups)\n" +" -B, --buggy-encrypt-proxy Equivalent to -E -W, provided for backwards\n" +" compatibility\n" +/*" -L, --tlsenforce Enforce TLSv1 connection (legacy)\n" +" -T, --no-ssl3 Do not connect using SSLv3 (legacy)\n"*/ " -z, --no-check-certificate Don't verify server SSL certificate\n" " -C, --cacert=STRING Path to trusted CA certificate or directory\n" #endif +" -4, --ipv4 Enforce IPv4 connection to local proxy\n" +" -6, --ipv6 Enforce IPv6 connection to local proxy\n" " -F, --passfile=STRING File with credentials for proxy authentication\n" " -P, --proxyauth=STRING Proxy auth credentials user:pass combination\n" " -R, --remproxyauth=STRING Remote proxy auth credentials user:pass combination\n" +#ifdef USE_SSL +" -c, --cert=FILENAME client SSL certificate (chain)\n" +" -k, --key=FILENAME client SSL key\n" +#endif // " -u, --user=STRING Username for proxy authentication\n" // " -s, --pass=STRING Password for proxy authentication\n" // " -U, --uservar=STRING Environment variable that holds username\n" @@ -83,7 +90,11 @@ void cmdline_parser_print_help (void) { " -N, --ntlm Use NTLM based authentication\n" " -t, --domain=STRING NTLM domain (default: autodetect)\n" " -H, --header=STRING Add additional HTTP headers to send to proxy\n" -" -o STRING send custom Host Header\n" +#ifdef USE_SSL +" -o, --host=STRING Send custom Host Header/SNI\n" +#else +" -o, --host=STRING Send custom Host Header\n" +#endif #ifdef SETPROCTITLE " -x, --proctitle=STRING Use a different process title\n" #endif @@ -135,17 +146,22 @@ int cmdline_parser( int argc, char * const *argv, struct gengetopt_args_info *ar args_info->remproxy_given = 0; args_info->remproxyauth_given = 0; args_info->verbose_given = 0; + args_info->quiet_given = 0; args_info->ntlm_given = 0; args_info->inetd_given = 0; - args_info->quiet_given = 0; + args_info->standalone_given = 0; + args_info->standalone_addr_given = 0; + args_info->standalone_iface_given = 0; args_info->header_given = 0; args_info->domain_given = 0; args_info->encrypt_given = 0; args_info->encryptproxy_given = 0; args_info->encryptremproxy_given = 0; + args_info->clientcert_given = 0; + args_info->clientkey_given = 0; args_info->wa_bug_29744_given = 0; args_info->proctitle_given = 0; - args_info->enforcetls1_given = 0; + /* args_info->enforcetls1_given = 0; */ args_info->host_given = 0; args_info->cacert_given = 0; @@ -165,20 +181,27 @@ int cmdline_parser( int argc, char * const *argv, struct gengetopt_args_info *ar args_info->remproxyauth_arg = NULL; \ args_info->header_arg[0] = '\0'; \ args_info->verbose_flag = 0; \ + args_info->quiet_flag = 0; \ args_info->ntlm_flag = 0; \ args_info->inetd_flag = 0; \ - args_info->quiet_flag = 0; \ - args_info->standalone_arg = 0; \ + args_info->standalone_arg = NULL; \ + args_info->standalone_addr = NULL; \ + args_info->standalone_iface = NULL; \ + args_info->standalone_port = 0; \ args_info->encrypt_flag = 0; \ args_info->encryptproxy_flag = 0; \ args_info->encryptremproxy_flag = 0; \ + args_info->clientcert_arg = NULL; \ + args_info->clientkey_arg = NULL; \ args_info->wa_bug_29744_flag = 0; \ - args_info->no_ssl3_flag = 0; \ + /* args_info->no_ssl3_flag = 0; */\ args_info->proctitle_arg = NULL; \ - args_info->enforcetls1_flag = 0; \ + /* args_info->enforcetls1_flag = 0; */\ args_info->host_arg = NULL; \ args_info->no_check_cert_flag = 0; \ args_info->cacert_arg = NULL; \ + args_info->enforceipv4_flag = 0; \ + args_info->enforceipv6_flag = 0; \ } clear_args(); @@ -199,11 +222,7 @@ int cmdline_parser( int argc, char * const *argv, struct gengetopt_args_info *ar static struct option long_options[] = { { "help", 0, NULL, 'h' }, { "version", 0, NULL, 'V' }, - { "user", 1, NULL, 'u' }, - { "pass", 1, NULL, 's' }, { "domain", 1, NULL, 't' }, -// { "uservar", 1, NULL, 'U' }, -// { "passvar", 1, NULL, 'S' }, { "passfile", 1, NULL, 'F' }, { "proxy", 1, NULL, 'p' }, { "proxyauth", 1, NULL, 'P' }, @@ -212,7 +231,7 @@ int cmdline_parser( int argc, char * const *argv, struct gengetopt_args_info *ar { "remproxyauth", 1, NULL, 'R' }, { "proctitle", 1, NULL, 'x' }, { "host", 1, NULL, 'o' }, - { "tlsenforce", 1, NULL, 'L' }, + { "tlsenforce", 0, NULL, 'L' }, { "header", 1, NULL, 'H' }, { "verbose", 0, NULL, 'v' }, { "ntlm", 0, NULL, 'N' }, @@ -222,17 +241,21 @@ int cmdline_parser( int argc, char * const *argv, struct gengetopt_args_info *ar { "encrypt", 0, NULL, 'e' }, { "encrypt-proxy", 0, NULL, 'E' }, { "encrypt-remproxy",0,NULL, 'X' }, + { "cert", 1, NULL, 'c' }, + { "key", 1, NULL, 'k' }, { "wa-bug-29744", 0, NULL, 'W' }, { "buggy-encrypt-proxy", 0, NULL, 'B' }, { "no-ssl3", 0, NULL, 'T' }, { "no-check-certificate",0,NULL,'z' }, { "cacert", 1, NULL, 'C' }, + { "ipv4", 0, NULL, '4' }, + { "ipv6", 0, NULL, '6' }, { NULL, 0, NULL, 0 } }; - c = getopt_long (argc, argv, "hVia:u:s:t:F:p:P:r:R:d:H:x:nvNeEXWBqLo:TzC:", long_options, &option_index); + c = getopt_long (argc, argv, "hVia:u:s:t:F:p:P:r:R:d:H:x:c:k:vNeEXWBqLo:TzC:46", long_options, &option_index); #else - c = getopt( argc, argv, "hVia:u:s:t:F:p:P:r:R:d:H:x:nvNeEXWBqLo:TzC:" ); + c = getopt( argc, argv, "hVia:u:s:t:F:p:P:r:R:d:H:x:c:k:vNeEXWBqLo:TzC:46" ); #endif if (c == -1) @@ -257,6 +280,26 @@ int cmdline_parser( int argc, char * const *argv, struct gengetopt_args_info *ar message("SSL client to proxy enabled\n"); break; + case 'c': /* client SSL certificate (chain) */ + if (args_info->clientcert_given) { + fprintf (stderr, "%s: '--cert' ('-c') option given more than once\n", PACKAGE); + clear_args (); + exit(1); + } + args_info->clientcert_given = 1; + args_info->clientcert_arg = gengetopt_strdup (optarg); + break; + + case 'k': /* client SSL key */ + if (args_info->clientkey_given) { + fprintf (stderr, "%s: '--key' ('-k') option given more than once\n", PACKAGE); + clear_args (); + exit(1); + } + args_info->clientkey_given = 1; + args_info->clientkey_arg = gengetopt_strdup (optarg); + break; + case 'W': /* if SSL is active stop it after CONNECT */ args_info->wa_bug_29744_flag = !(args_info->wa_bug_29744_flag); if( args_info->verbose_flag ) @@ -281,16 +324,18 @@ int cmdline_parser( int argc, char * const *argv, struct gengetopt_args_info *ar break; case 'a': /* Run as standalone daemon */ + if (args_info->standalone_given) { + fprintf (stderr, "%s: '--standalone' ('-a') option given more than once\n", PACKAGE); + clear_args (); + exit(1); + } if ( args_info->inetd_flag ) { fprintf( stderr, "%s: `--standalone' (`-a') conflicts with `--inetd' (`-i')\n", PACKAGE ); clear_args(); exit(1); } - if ( ( args_info->standalone_arg = atoi( optarg ) ) < 1 ) { - fprintf( stderr, "%s: Illegal port value for `--standalone' (`-a')\n", PACKAGE); - clear_args(); - exit(1); - } + args_info->standalone_given = 1; + args_info->standalone_arg = gengetopt_strdup (optarg); break; case 'V': /* Print version and exit. */ @@ -305,40 +350,18 @@ int cmdline_parser( int argc, char * const *argv, struct gengetopt_args_info *ar break; case 'L': - args_info->enforcetls1_given = 1; - message("Enforcing TLSv1"); - args_info->enforcetls1_flag = 1; + /* args_info->enforcetls1_given = 1; + message("Enforcing TLSv1\n"); + args_info->enforcetls1_flag = 1; */ + message ("Option -L/--tlsenforce is deprecated and without effect\n"); break; case 'o': args_info->host_given = 1; - message("Host-header override enabled\n"); + message("Host-header/SNI override enabled\n"); args_info->host_arg = gengetopt_strdup (optarg); break; - case 'u': /* Username to send to HTTPS proxy for authentication. */ - if (args_info->user_given) { - fprintf (stderr, "%s: `--user' (`-u'), `--proxyauth' (`-P') or `--passfile' (`-F') option given more than once\n", PACKAGE); - clear_args (); - exit(1); - } - args_info->user_given = 1; - args_info->user_arg = gengetopt_strdup (optarg); - message ("Option -u/--user is deprecated, please use -P/--proxyauth user:pass\n"); - break; - - - case 's': /* Password to send to HTTPS proxy for authentication. */ - if (args_info->pass_given) { - fprintf (stderr, "%s: `--pass' (`-s') or `--passfile' (`-F') option given more than once\n", PACKAGE); - clear_args (); - exit(1); - } - args_info->pass_given = 1; - args_info->pass_arg = gengetopt_strdup (optarg); - message ("Option -s/--pass is deprecated, please use -P/--proxyauth user:pass\n"); - break; - case 't': /* Env Var with NTLM DOMAIN (when overriding). */ if (args_info->domain_given) { fprintf (stderr, "%s: `--domain' (`-t') option given more than once\n", PACKAGE); @@ -425,9 +448,10 @@ int cmdline_parser( int argc, char * const *argv, struct gengetopt_args_info *ar break; case 'T': /* Turn off SSLv3 */ - args_info->no_ssl3_flag = !(args_info->no_ssl3_flag); + /* args_info->no_ssl3_flag = !(args_info->no_ssl3_flag); if( args_info->verbose_flag ) - message("SSLv3 disabled\n"); + message("SSLv3 disabled\n"); */ + message ("Option -T/--no-ssl3 is deprecated and without effect\n"); break; case 'd': /* Destination host to built the tunnel to. */ @@ -478,6 +502,28 @@ int cmdline_parser( int argc, char * const *argv, struct gengetopt_args_info *ar args_info->cacert_arg = gengetopt_strdup (optarg); break; + case '4': /* Enforce IPv4 */ + if ( args_info->enforceipv6_flag ) { + fprintf( stderr, "%s: `--ipv4' (`-4') conflicts with `--ipv6' (`-6')\n", PACKAGE ); + clear_args(); + exit(1); + } + args_info->enforceipv4_flag = 1; + if( args_info->verbose_flag ) + message("IPv4 enforced\n"); + break; + + case '6': /* Enforce IPv6 */ + if ( args_info->enforceipv4_flag ) { + fprintf( stderr, "%s: `--ipv6' (`-6') conflicts with `--ipv4' (`-4')\n", PACKAGE ); + clear_args(); + exit(1); + } + args_info->enforceipv6_flag = 1; + if( args_info->verbose_flag ) + message("IPv6 enforced\n"); + break; + case 0: /* Long option with no short option */ case '?': /* Invalid option. */ @@ -520,7 +566,7 @@ int cmdline_parser( int argc, char * const *argv, struct gengetopt_args_info *ar if ( args_info->remuser_arg == NULL ) { if ( (tmp = getenv("REMPROXYUSER")) != NULL ) { args_info->remuser_given = 1; - args_info->user_arg = gengetopt_strdup (tmp); + args_info->remuser_arg = gengetopt_strdup (tmp); if( args_info->verbose_flag ) message( "Found remote user '%s' in env variable REMPROXYPASS.\n", args_info->remuser_arg); } @@ -528,7 +574,7 @@ int cmdline_parser( int argc, char * const *argv, struct gengetopt_args_info *ar if ( args_info->rempass_arg == NULL ) { if ( (tmp = getenv("REMPROXYPASS")) != NULL ) { args_info->rempass_given = 1; - args_info->user_arg = gengetopt_strdup (tmp); + args_info->rempass_arg = gengetopt_strdup (tmp); if( args_info->verbose_flag ) message( "Found remote password in env variable REMPROXYPASS.\n" ); } @@ -550,13 +596,20 @@ int cmdline_parser( int argc, char * const *argv, struct gengetopt_args_info *ar } } - if (! args_info->proxy_given && ! args_info->dest_given ) { + if (! args_info->proxy_given || ! args_info->dest_given ) { clear_args (); // cmdline_parser_print_help (); message( "No proxy or destination given, exiting\nUse '--help' flag for usage info\n" ); exit(1); } + if ( args_info->clientcert_given ^ args_info->clientkey_given ) { + clear_args (); + message( "Both of '--cert' ('-c') and '--key' ('-k') must be specified\n" ); + exit(1); + } + + /* Parse -p/--proxy information */ if (args_info->proxy_given ) { char proxy_arg_fmt[32]; size_t proxy_arg_len; @@ -581,7 +634,56 @@ int cmdline_parser( int argc, char * const *argv, struct gengetopt_args_info *ar args_info->proxyhost_given = 1; args_info->proxyport_given = 1; } else { - message( "parse_cmdline: could not find your proxy hostname/ip (%s)\n", args_info->proxy_arg ); + message( "parse_cmdline: specified proxy (%s) does not fit the expected pattern hostname/ip:port\n", args_info->proxy_arg ); + missing_required_options++; + } + } + + /* Parse -a/--standalone information */ + if ( args_info->standalone_given ) { + char standalone_arg_fmt[32]; + size_t standalone_arg_len; + char *aaddr; + char *aiface; + int aport; + + standalone_arg_len = strlen( args_info->standalone_arg ); + if ( (aaddr = malloc( standalone_arg_len + 1 )) == NULL ) { + message( "Out of memory\n" ); + exit(1); + } + if ( (aiface = malloc( standalone_arg_len + 1 )) == NULL ) { + message( "Out of memory\n" ); + exit(1); + } + /* try IPv4 literal and port */ + snprintf( standalone_arg_fmt, sizeof(standalone_arg_fmt), "%%%zu[0-9.]:%%5u", standalone_arg_len - 1 ); + r = sscanf( args_info->standalone_arg, standalone_arg_fmt, aaddr, &aport ); + if ( r != 2 ) { + /* try bracket-enclosed IPv6 literal and port */ + snprintf( standalone_arg_fmt, sizeof(standalone_arg_fmt), "[%%%zu[0-9A-Fa-f:]]:%%5u", standalone_arg_len - 1 ); + r = sscanf( args_info->standalone_arg, standalone_arg_fmt, aaddr, &aport ); + } + if ( r != 2 ) { + /* try bracket-enclosed IPv6 literal, interface and port */ + snprintf( standalone_arg_fmt, sizeof(standalone_arg_fmt), "[%%%zu[0-9A-Fa-f:]%%%%%%%zu[^]]]:%%5u", standalone_arg_len - 1, standalone_arg_len - 1 ); + if ( sscanf( args_info->standalone_arg, standalone_arg_fmt, aaddr, aiface, &aport ) == 3 ) + r = 3; + } + if ( r == 3 ) { + args_info->standalone_iface = aiface; + args_info->standalone_iface_given = 1; + r--; + } + if ( r == 2 ) { + args_info->standalone_addr = aaddr; + args_info->standalone_port = aport; + args_info->standalone_addr_given = 1; + /* try port only */ + } else if ( sscanf( args_info->standalone_arg, "%5u", &aport ) ) { + args_info->standalone_port = aport; + } else { + message( "parse_cmdline: specified standalone argument (%s) does not fit one of the expected patterns: port, ipv4:port, [ipv6]:port, [ipv6%%interface]:port\n", args_info->standalone_arg ); missing_required_options++; } } @@ -591,10 +693,10 @@ int cmdline_parser( int argc, char * const *argv, struct gengetopt_args_info *ar char *puser = NULL; char *ppass = NULL; - puser = malloc( 24+1 ); - ppass = malloc( 24+1 ); + puser = malloc( 80+1 ); + ppass = malloc( 80+1 ); - r = sscanf( args_info->proxyauth_arg, "%24[^:]:%24s", puser, ppass ); + r = sscanf( args_info->proxyauth_arg, "%80[^:]:%80s", puser, ppass ); if ( r == 2 ) { args_info->user_arg = puser; args_info->pass_arg = ppass; @@ -614,10 +716,10 @@ int cmdline_parser( int argc, char * const *argv, struct gengetopt_args_info *ar char *ruser = NULL; char *rpass = NULL; - ruser = malloc( 24+1 ); - rpass = malloc( 24+1 ); + ruser = malloc( 80+1 ); + rpass = malloc( 80+1 ); - r = sscanf( args_info->remproxyauth_arg, "%24[^:]:%24s", ruser, rpass ); + r = sscanf( args_info->remproxyauth_arg, "%80[^:]:%80s", ruser, rpass ); if ( r == 2 ) { args_info->remuser_arg = ruser; args_info->rempass_arg = rpass; @@ -631,6 +733,7 @@ int cmdline_parser( int argc, char * const *argv, struct gengetopt_args_info *ar missing_required_options++; } } + if ( missing_required_options ) exit(1); diff --git a/cmdline.h b/cmdline.h index 6ce019a..93cf460 100644 --- a/cmdline.h +++ b/cmdline.h @@ -23,7 +23,7 @@ #ifndef _cmdline_h #define _cmdline_h -#define MAX_HEADER_SIZE 1024 +#define MAX_HEADER_SIZE 4096 struct gengetopt_args_info { char *user_arg; /* Username to send to HTTPS proxy for auth. */ @@ -40,19 +40,26 @@ struct gengetopt_args_info { char *remproxy_arg; /* Remote proxy to tunnel to. */ char *remproxyauth_arg; /* Remote proxy auth. */ int verbose_flag; /* Turn on verbosity (default=off). */ + int quiet_flag; /* Turn on quiet mode (default=off). */ int ntlm_flag; /* Turn on ntlm (default=off). */ int inetd_flag; /* Turn on inetd (default=off). */ - int quiet_flag; /* Turn on quiet mode (default=off). */ - int standalone_arg; /* Turn on stdalone (-a) on port */ + char *standalone_arg; /* Turn on standalone (-a) on [addr:]port */ + char *standalone_addr; + char *standalone_iface; + int standalone_port; int encrypt_flag; /* Turn on SSL encryption (default=off). */ int encryptproxy_flag; /* Turn on client to proxy SSL encryption (def=off).*/ int encryptremproxy_flag; /* Turn on local to remote proxy SSL encryption (def=off).*/ + char *clientcert_arg; /* client SSL certificate */ + char *clientkey_arg; /* client SSL key */ int wa_bug_29744_flag; /* Use SSL encryption only until CONNECT, if at all (def=off).*/ - int no_ssl3_flag; /* Turn off SSLv3 (default=on) */ + /* int no_ssl3_flag; Turn off SSLv3 (default=on) */ char *proctitle_arg; /* Override process title (default=off). */ - int enforcetls1_flag; /* Override default and enforce TLSv1 */ + /* int enforcetls1_flag; Override default and enforce TLSv1 */ char *host_arg; /* Optional Host Header */ int no_check_cert_flag; /* Turn off server SSL certificate verification (default=on) */ + int enforceipv4_flag; /* Enforce IPv4 (default=off). */ + int enforceipv6_flag; /* Enforce IPv6 (default=off). */ char *cacert_arg; /* Trusted CA certificate (or directory) for server SSL certificate verification */ int help_given; /* Whether help was given. */ int version_given; /* Whether version was given. */ @@ -69,16 +76,21 @@ struct gengetopt_args_info { int remproxy_given; /* Whether remproxy was given. */ int remproxyauth_given; /* Whether remproxy was given. */ int verbose_given; /* Whether verbose was given. */ + int quiet_given; /* Whether quiet mode was given. */ int ntlm_given; /* Whether ntlm was given. */ int inetd_given; /* Whether inetd was given. */ - int quiet_given; /* Whether quiet mode was given. */ + int standalone_given; /* Whether standalone was given */ + int standalone_addr_given; /* Whether standalone address was given */ + int standalone_iface_given; /* Whether standalone interface was given */ int header_given; /* Whether extra headers are given */ int encrypt_given; /* Whether encrypt was given */ int encryptproxy_given; /* Whether encrypt was given */ int encryptremproxy_given; /* Whether encrypt was given */ + int clientcert_given; /* Whether client SSL certificate was given */ + int clientkey_given; /* Whether client SSL key was given */ int wa_bug_29744_given; /* Whether work around was given */ int proctitle_given; /* Whether to override process title */ - int enforcetls1_given; /* Wheter to enforce TLSv1 */ + /* int enforcetls1_given; Wheter to enforce TLSv1 */ int host_given; /* Wheter we override the Host Header */ int cacert_given; /* Whether cacert was given */ }; diff --git a/config.h b/config.h index 242236e..f73e2ed 100644 --- a/config.h +++ b/config.h @@ -1,5 +1,5 @@ -/* Proxytunnel - (C) 2001-2008 Jos Visser / Mark Janssen */ -/* Contact: josv@osp.nl / maniac@maniac.nl */ +/* Proxytunnel - (C) 2001-2023 Jos Visser / Mark Janssen */ +/* Contact: josv@osp.nl / mark@sig-io.nl */ /* * This program is free software; you can redistribute it and/or modify @@ -17,7 +17,9 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#define VERSION "1.10.20210128" +#define VERSION "1.12.3" +#define VERSION_YEAR "2025" +#define VERSION_DATE "2025-03-07" #define PACKAGE "proxytunnel" #define PURPOSE "Build generic tunnels through HTTPS proxies" #define AUTHORS "Jos Visser (Muppet) , Mark Janssen (Maniac) " diff --git a/docs/Makefile b/docs/Makefile index f313c38..8e71490 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -5,6 +5,9 @@ mandir = $(datadir)/man adoctargets = $(shell echo *.adoc) htmltargets = $(patsubst %.adoc, %.html, $(adoctargets)) +version = $(shell grep ' VERSION ' ../config.h | cut -d'"' -f2) +version_date = $(shell grep ' VERSION_DATE ' ../config.h | cut -d'"' -f2) + docs: proxytunnel.1 $(htmltargets) install: proxytunnel.1 @@ -15,7 +18,7 @@ clean: rm -f proxytunnel.1 *.html *.xml %.1.html: %.1.adoc - asciidoc -d manpage $< + asciidoc -d manpage -arevnumber=$(version) -arevdate=$(version_date) $< %.1: %.1.xml xmlto man $< @@ -24,4 +27,4 @@ clean: asciidoc $< %.1.xml: %.1.adoc - asciidoc -b docbook -d manpage $< + asciidoc -b docbook -d manpage -arevnumber=$(version) -arevdate=$(version_date) $< diff --git a/docs/proxytunnel.1.adoc b/docs/proxytunnel.1.adoc index 7d136ee..0e8e6b5 100644 --- a/docs/proxytunnel.1.adoc +++ b/docs/proxytunnel.1.adoc @@ -1,6 +1,4 @@ = proxytunnel(1) -Proxytunnel developers -v1.9.0, Augustus 2008 == NAME @@ -8,7 +6,7 @@ proxytunnel - program to tunnel a connection through a standard HTTPS proxy == SYNOPSIS -*proxytunnel* _[OPTION]_... +*proxytunnel* [_OPTION…_] == DESCRIPTION @@ -21,44 +19,48 @@ also be used for other proxy-traversing purposes like proxy bouncing. == OPTIONS *-i*, *--inetd*:: - Run from inetd (default: off) + Run from inetd (default: off). -*-a*, *--standalone*=_port_:: - Run as standalone daemon on specified _port_ +*-a*, *--standalone*=++[++_address_++:]++_port_:: + Run as standalone daemon on specified _address_ and _port_. _address_ may + be a IPv4 address, a bracket-enclosed IPv6 address or a bracket-enclosed + combination of IPv6 address, \'%' and interface name. The latter format is + only required with link-local IPv6 addresses. The daemon listens on any + address if _address_ is not given. + *Examples*::: + 22, 123.45.67.89:22, [2001:db8::123:4567:89ab:cdef]:22, + [2001:db8::123:4567:89ab:cdef%eth0]:22 -*-p*, *--proxy*=_host_:_port_:: - Use _host_ and _port_ as the local proxy to connect to, if not specified - the *HTTP_PROXY* environment variable, if set, will be used instead +*-p*, *--proxy*=_host_++:++_port_:: + Use _host_ and _port_ as the local (primary) proxy to connect to, if not + specified the *HTTP_PROXY* environment variable, if set, will be used + instead. This option or the environment variable are mandatory. -*-r*, *--remproxy*=_host_:_port_:: - Use _host_ and _port_ as the remote (secondary) proxy to connect to +*-r*, *--remproxy*=_host_++:++_port_:: + Use _host_ and _port_ as the remote (secondary) proxy to connect to. -*-d*, *--dest*=_host_:_port_:: - Use _host_ and _port_ as the destination for the tunnel, you can also - specify them as the argument to the proxytunnel command +*-d*, *--dest*=_host_++:++_port_:: + Use _host_ and _port_ as the destination for the tunnel. This is a + mandatory option. *-e*, *--encrypt*:: - SSL encrypt data between local proxy and destination + SSL encrypt data between local proxy and destination. *-E*, *--encrypt-proxy*:: - SSL encrypt data between client and local proxy + SSL encrypt data between client and local proxy. *-X*, *--encrypt-remproxy*:: - SSL encrypt data between local and remote (secondary) proxy - -*-W*, *--wa-bug-29744*:: - If SSL is in use (by *-e*, *-E*, *-X* options), stop using it - immediately after the CONNECT exchange to workaround apache server - bugs. (This might not work on all setups; see - /usr/share/doc/proxytunnel/README.Debian.gz for more details.) - -*-B*, *--buggy-encrypt-proxy*:: - Equivalent to *-E -W*. (Provided for backwards compatibility.) + SSL encrypt data between local and remote (secondary) proxy. == ADDITIONAL OPTIONS -*-T*, *--no-ssl3*:: - Prevent the use of SSLv3 in encrypted connections (default: enabled) +*-W*, *--wa-bug-29744*:: + Workaround ASF Bugzilla 29744: If SSL is in use (by *-e*, *-E*, *-X* + options), stop using it immediately after the CONNECT exchange to + workaround apache server bugs (This might not work on all setups). + +*-B*, *--buggy-encrypt-proxy*:: + Equivalent to *-E -W* (Provided for backwards compatibility). *-z*, *--no-check-certificate*:: Do not verify server SSL certificate when establishing an SSL connection. @@ -69,63 +71,84 @@ also be used for other proxy-traversing purposes like proxy bouncing. *-C*, *--cacert*=_filename/directory_:: Specify a CA certificate file (or directory containing CA certificate(s)) to trust when verifying a server SSL certificate. If a directory is provided, - it must be prepared with OpenSSL's c_rehash tool. (default: /etc/ssl/certs) + it must be prepared with OpenSSL's c_rehash tool (default, unless changed at + compile time using DEFAULT_CA_FILE or DEFAULT_CA_DIR options: /etc/ssl/certs). + +*-4*, *--ipv4*:: + Enforce the use of IPv4 when connecting to the local proxy. + +*-6*, *--ipv6*:: + Enforce the use of IPv6 when connecting to the local proxy. *-F*, *--passfile*=_filename_:: Use _filename_ for reading username and password for HTTPS proxy authentication, the file uses the same format as .wgetrc and can be shared with wget. Use this option, or environment variables to hide the password - from other users + from other users. -*-P*, *--proxyauth*=_username_:_password_:: +*-P*, *--proxyauth*=_username_++:++_password_:: Use _username_ and _password_ as credentials to authenticate against a local HTTPS proxy, the username and password can also be specified in the *PROXYUSER* and *PROXYPASS* environment variables to hide them from other users. If the _password_ is omitted and no *PROXYPASS* environment variable is - set, proxytunnel will prompt for a password + set, proxytunnel will prompt for a password. -*-R*, *--remproxyauth*=_username_:_password_:: +*-R*, *--remproxyauth*=_username_++:++_password_:: Use _username_ and _password_ as credentials to authenticate against a remote (secondary) HTTPS proxy, the username and password can also be specified in the *REMPROXYUSER* and *REMPROXYPASS* environment variables to hide them from other users. If the _password_ is omitted and no *REMPROXYPASS* environment variable is - set, proxytunnel will prompt for a password + set, proxytunnel will prompt for a password. + +*-c*, *--cert*=_filename_:: + Provide the name of the file containing the SSL client certificate to + authenticate by client certificate against local proxy, remote proxy or + destination. The file must be in PEM format. + On top of this it may contain one or more intermediary certificates missing + at the servers's end, effectively forming a certificate chain. + Requires specification of *-k*, *--key* in addition. + Ignored if neither *-e*, *--encrypt* nor *-E*, *--encrypt-proxy* nor + *-X*, *--encrypt-remproxy* is given. + +*-k*, *--key*=_filename_:: + Provide the name of the file containing the SSL client key to authenticate + by client certificate against local proxy, remote proxy or destination. The + file must be in PEM format. + Requires specification of *-c*, *--cert* in addition. + Ignored if neither *-e*, *--encrypt* nor *-E*, *--encrypt-proxy* nor + *-X*, *--encrypt-remproxy* is given. *-N*, *--ntlm*:: - Use NTLM basd authentication + Use NTLM based authentication. *-t*, *--domain*=_STRING_:: - Specify NTLM domain (default: autodetect) + Specify NTLM domain (default: autodetect). *-H*, *--header*=_STRING_:: - Add additional HTTP headers to send to proxy + Add additional HTTP headers to send to proxy. + +*-o*, *--host*=_host_++[:++_port_]:: + Send a custom Host header. With SSL connections _host_ is also sent as SNI. *-x*, *--proctitle*=_STRING_:: - Use a different process title + Use a different process title. == MISCELLANEOUS OPTIONS *-v*, *--verbose*:: - Turn on verbosity + Turn on verbosity. *-q*, *--quiet*:: - Suppress messages + Suppress messages. *-h*, *--help*:: - Print help and exit + Print help and exit. *-V*, *--version*:: - Print version and exit - - -== ARGUMENTS -_host_:_port_ is the destination hostname and port number combination - -NOTE: Specifying the destination as arguments is exactly the same as -specifying them using the *-d* or *--dest* option. + Print version and exit. == USAGE @@ -165,7 +188,7 @@ Host system.athome.nl NOTE: The +ServerAliveInterval+ directive makes sure that idle connections are not being dropped by intermediate firewalls that remove active sessions -aggresively. If you see your connection dropping out, try to lower the value +aggressively. If you see your connection dropping out, try to lower the value even more. To use the dynamic (SOCKS) portforwarding capability of the SSH client, you @@ -192,27 +215,27 @@ variables: *HTTP_PROXY*:: If this environment variable is set, proxytunnel will use it as the - _local proxy_ if *-p* or *--proxy* is not provided + _local proxy_ if *-p* or *--proxy* is not provided. *PROXYUSER*:: If this environment variable is set, proxytunnel will use it as the _username_ for proxy authentication, unless specified using the *-P* or - *--proxyauth* option + *--proxyauth* option. *PROXYPASS*:: If this environment variable is set, proxytunnel will use it as the _password_ for proxy authentication, unless specified using the *-P* or - *--proxyauth* option + *--proxyauth* option. *REMPROXYUSER*:: If this environment variable is set, proxytunnel will use it as the _username_ for remote (secondary) proxy authentication, unless specified - using the *-R* or *--remproxyauth* option + using the *-R* or *--remproxyauth* option. *REMPROXYPASS*:: If this environment variable is set, proxytunnel will use it as the _password_ for remote (secondary) proxy authentication, unless specified - using the *-R* or *--remproxyauth* option + using the *-R* or *--remproxyauth* option. == SEE ALSO @@ -221,7 +244,8 @@ variables: == BUGS This software is bug-free, at least we'd like to think so. If you do not -agree with us, please attach the proof to your friendly email :) +agree with us, please provide the proof with your friendly report at +https://github.com/proxytunnel/proxytunnel/issues :) == AUTHOR @@ -230,4 +254,4 @@ This manpage was initially written by Loïc Le Guyader asciidoc by Dag Wieërs and is now maintained by the Proxytunnel developers. -Homepage at http://proxytunnel.sourceforge.net/ +Homepages at https://proxytunnel.sourceforge.io and https://github.com/proxytunnel/proxytunnel diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..d28337e --- /dev/null +++ b/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1743550720, + "narHash": "sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "c621e8422220273271f52058f618c94e405bb0f5", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1743583204, + "narHash": "sha256-F7n4+KOIfWrwoQjXrL2wD9RhFYLs2/GGe/MQY1sSdlE=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "2c8d3f48d33929642c1c12cd243df4cc7d2ce434", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "lastModified": 1743296961, + "narHash": "sha256-b1EdN3cULCqtorQ4QeWgLMrd5ZGOjLSLemfa00heasc=", + "owner": "nix-community", + "repo": "nixpkgs.lib", + "rev": "e4822aea2a6d1cdd36653c134cacfd64c97ff4fa", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nixpkgs.lib", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-parts": "flake-parts", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..eb73249 --- /dev/null +++ b/flake.nix @@ -0,0 +1,33 @@ +{ + description = "Basic flake that provides proxytunnel as a package or as a binary in a nix shell"; + + inputs = { + flake-parts.url = "github:hercules-ci/flake-parts"; + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + }; + + outputs = inputs @ {flake-parts, ...}: + flake-parts.lib.mkFlake {inherit inputs;} { + # TODO: Add support for more systems once checked. + systems = ["x86_64-linux"]; + + imports = [inputs.flake-parts.flakeModules.easyOverlay]; + + perSystem = { + config, + pkgs, + ... + }: { + overlayAttrs = { + inherit (config.packages) proxytunnel; + }; + + packages.proxytunnel = pkgs.callPackage ./nix/proxytunnel.nix {}; + packages.default = config.packages.proxytunnel; + + devShells.default = pkgs.mkShell { + packages = [config.packages.default]; + }; + }; + }; +} diff --git a/http.c b/http.c index a4e020c..12e1956 100644 --- a/http.c +++ b/http.c @@ -37,17 +37,16 @@ * header */ void analyze_HTTP(PTSTREAM *pts) { - char *p = strtok( buf, " "); + char *p; /* Strip html error pages for faulty proxies (Stephane Engel ) */ - while (strncmp( p, "HTTP/", 5) != 0 ) { - if ( readline(pts) ) { - p = strtok( buf, " "); - } else { + do { + if (readline(pts) <= 0) { message( "analyze_HTTP: readline failed: Connection closed by remote host\n" ); exit(2); } - } + p = strtok( buf, " \t"); + } while (strncmp( p, "HTTP/", 5) != 0 ); if (strcmp( p, "HTTP/1.0" ) != 0 && strcmp( p, "HTTP/1.1" ) != 0) { message( "Unsupported HTTP version number %s\n", p ); @@ -117,6 +116,7 @@ void proxy_protocol(PTSTREAM *pts) { if (args_info.ntlm_flag) { if (ntlm_challenge == 1) { build_type3_response(); + ntlm_challenge = 2; strzcat( buf, "Proxy-Authorization: NTLM %s\r\n", ntlm_type3_buf ); } else if (ntlm_challenge == 0) { strzcat( buf, "Proxy-Authorization: NTLM %s\r\n", ntlm_type1_buf ); @@ -157,7 +157,7 @@ void proxy_protocol(PTSTREAM *pts) { /* Read the first line of the response and analyze it */ analyze_HTTP(pts); - if (args_info.remproxy_given ) { + if (ntlm_challenge < 3 && args_info.remproxy_given ) { /* Clean buffer for next analysis */ while ( strcmp( buf, "\r\n" ) != 0 ) readline(pts); @@ -209,8 +209,8 @@ void proxy_protocol(PTSTREAM *pts) { * Then, repeat reading lines of the responses until a blank line * (which signifies the end of the response) is encountered. */ - if (ntlm_challenge == 1) { - ntlm_challenge = 2; + if (ntlm_challenge == 2) { + ntlm_challenge = 3; } else { do { readline(pts); diff --git a/io.c b/io.c index e8df31a..56d516e 100644 --- a/io.c +++ b/io.c @@ -57,7 +57,7 @@ int readline(PTSTREAM *pts) { if( args_info.verbose_flag ) { /* Copy line of data into dstr without trailing newline */ - char *dstr = malloc(strlen(buf) + 1); + char *dstr = calloc(1, strlen(buf) + 1); strncpy( dstr, buf, strlen(buf)); if (strcmp(dstr, "")) message( " <- %s\n", dstr ); diff --git a/nix/proxytunnel.nix b/nix/proxytunnel.nix new file mode 100644 index 0000000..ad3a010 --- /dev/null +++ b/nix/proxytunnel.nix @@ -0,0 +1,31 @@ +{ + gnu-system ? true, + set-proc-title ? true, + pkgs, +}: let + optflags = "-DUSE_SSL ${ + if gnu-system + then "-DHAVE_GETOPT_LONG" + else "" + } ${ + if set-proc-title + then "-DSETPROCTITLE -DSPT_TYPE=2" + else "" + }"; +in + pkgs.stdenv.mkDerivation { + pname = "proxytunnel"; + version = "1.12.3"; + src = ./..; + + buildInputs = [pkgs.openssl]; + + buildPhase = '' + make OPTFLAGS="${optflags}" + ''; + + installPhase = '' + mkdir -p $out/bin + cp ./proxytunnel $out/bin + ''; + } diff --git a/ntlm.c b/ntlm.c index 13928c4..5159202 100644 --- a/ntlm.c +++ b/ntlm.c @@ -28,14 +28,28 @@ #include "proxytunnel.h" #include #include -#include -#include +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + #ifdef CYGWIN + #include + #endif + #include + #include +#else + #include + #include +#endif #define TYPE1_DATA_SEG 8 #define TYPE2_BUF_SIZE 2048 #define DOMAIN_BUFLEN 256 #define LM2_DIGEST_LEN 24 +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +const EVP_MD *md4alg; +const EVP_MD *md5alg; +EVP_MD_CTX *mdctx; +#endif + int ntlm_challenge = 0; void message( char *s, ... ); int unicode = 0; @@ -54,19 +68,72 @@ int bloblen; unsigned char *t_info; int t_info_len; -unsigned long flags; +uint32_t flags; unsigned char lm2digest[LM2_DIGEST_LEN]; +void init_ntlm() { +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + OSSL_PROVIDER *provider; + provider = OSSL_PROVIDER_load(NULL, "default"); + if (!provider) { + my_perror("Loading default provider failed"); + exit(1); + } + provider = OSSL_PROVIDER_load(NULL, "legacy"); +#ifdef CYGWIN + if (!provider) { + // available at msys and git for windows + // the msys version has an additional dependency on libcrypto-3-x64.dll + provider = OSSL_PROVIDER_load(NULL, "/mingw64/lib/ossl-modules/legacy.dll"); + } + if (!provider) { + // available at msys (without dependency on libcrypto-3-x64.dll) + provider = OSSL_PROVIDER_load(NULL, "/usr/lib/ossl-modules/legacy.dll"); + } + if (!provider) { + // default installation path for additional tools + provider = OSSL_PROVIDER_load(NULL, "/usr/local/bin/legacy.dll"); + } + if (!provider) { + // directory of proxytunnel itself + const char *p = strrchr(program_name, '/'); + if (p) { + const int len = p - program_name; + char *tmp = (char*)alloca(len + sizeof("/legacy.dll")); + memcpy(tmp, program_name, len); + strcpy(tmp + len, "/legacy.dll"); + provider = OSSL_PROVIDER_load(NULL, tmp); + } + } + if (!provider) { + // current working directory + char *cwd = getcwd(NULL, 0); + if (cwd) { + const int len = strlen(cwd); + char *tmp = (char*)alloca(len + sizeof("/legacy.dll")); + memcpy(tmp, cwd, len); + free(cwd); + strcpy(tmp + len, "/legacy.dll"); + provider = OSSL_PROVIDER_load(NULL, tmp); + } + } +#endif + if (!provider) { + my_perror("Loading legacy provider failed"); + exit(1); + } + md4alg = EVP_md4(); + md5alg = EVP_md5(); + mdctx = EVP_MD_CTX_new(); +#endif +} + void build_type1() { ntlm_type1 *type1; int len = sizeof(ntlm_type1) + sizeof(unsigned char) * TYPE1_DATA_SEG; - type1 = (ntlm_type1 *)malloc(len); - if (!type1) { - message("Fatal Error in build type1, Malloc failed\n"); - exit(-1); - } + type1 = (ntlm_type1 *)alloca(len); memset(type1, 0, len); type1->signature[0] = 'N'; @@ -83,7 +150,6 @@ void build_type1() { base64((unsigned char *)ntlm_type1_buf, (unsigned char *)type1, len); - free(type1); return; } @@ -181,11 +247,7 @@ void build_type3_response() { len = sizeof(ntlm_type3) + sizeof(unsigned char) * (LM2_DIGEST_LEN + bloblen + (strlen(domain) + strlen(args_info.user_arg) + strlen(workstation)) * sp); - type3 = (ntlm_type3 *)malloc(len); - if (!type3) { - message("Fatal Error in build type3, Malloc failed\n"); - exit(-1); - } + type3 = (ntlm_type3 *)alloca(len); t3 = (unsigned char *) type3; memset(type3, 0, len); @@ -231,7 +293,6 @@ void build_type3_response() { base64((unsigned char *)ntlm_type3_buf, (unsigned char *)type3, len); - free(type3); return; } @@ -247,7 +308,10 @@ unsigned char* key; /* pointer to authentication key */ int key_len; /* length of authentication key */ unsigned char digest[16]; /* caller digest to be filled in */ { +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#else MD5_CTX context; +#endif unsigned char k_ipad[65]; /* inner padding - key XORd with ipad */ unsigned char k_opad[65]; /* outer padding - key XORd with opad */ unsigned char tk[16]; @@ -255,10 +319,15 @@ unsigned char digest[16]; /* caller digest to be filled in */ /* if key is longer than 64 bytes reset it to key=MD5(key) */ if (key_len > 64) { - MD5_CTX tctx; - MD5_Init( &tctx ); - MD5_Update( &tctx, key, key_len ); - MD5_Final( tk, &tctx ); +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + EVP_DigestInit_ex(mdctx, md5alg, NULL); + EVP_DigestUpdate(mdctx, key, key_len); + EVP_DigestFinal_ex(mdctx, tk, NULL); +#else + MD5_Init(&context); + MD5_Update(&context, key, key_len); + MD5_Final(tk, &context); +#endif key = tk; key_len = 16; } @@ -275,10 +344,10 @@ unsigned char digest[16]; /* caller digest to be filled in */ */ /* start out by storing key in pads */ - bzero( k_ipad, sizeof k_ipad); - bzero( k_opad, sizeof k_opad); - bcopy( key, k_ipad, key_len); - bcopy( key, k_opad, key_len); + memset(k_ipad, 0, sizeof(k_ipad)); + memset(k_opad, 0, sizeof(k_opad)); + memcpy(k_ipad, key, key_len); + memcpy(k_opad, key, key_len); /* XOR key with ipad and opad values */ for (i=0; i<64; i++) { @@ -287,22 +356,39 @@ unsigned char digest[16]; /* caller digest to be filled in */ } /* perform inner MD5 */ +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + EVP_DigestInit_ex(mdctx, md5alg, NULL); /* init context for 1st pass */ + EVP_DigestUpdate(mdctx, k_ipad, 64); /* start with inner pad */ + EVP_DigestUpdate(mdctx, text, text_len); /* then text of datagram */ + EVP_DigestFinal_ex(mdctx, digest, NULL); /* finish up 1st pass */ +#else MD5_Init(&context); /* init context for 1st pass */ MD5_Update(&context, k_ipad, 64); /* start with inner pad */ MD5_Update(&context, text, text_len); /* then text of datagram */ MD5_Final(digest, &context); /* finish up 1st pass */ +#endif /* perform outer MD5 */ +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + EVP_DigestInit_ex(mdctx, md5alg, NULL); /* init context for 1st pass */ + EVP_DigestUpdate(mdctx, k_opad, 64); /* start with inner pad */ + EVP_DigestUpdate(mdctx, digest, 16); /* then text of datagram */ + EVP_DigestFinal_ex(mdctx, digest, NULL); /* finish up 1st pass */ +#else MD5_Init(&context); /* init context for 2nd pass */ MD5_Update(&context, k_opad, 64); /* start with outer pad */ MD5_Update(&context, digest, 16); /* then results of 1st hash */ MD5_Final(digest, &context); /* finish up 2nd pass */ +#endif } void build_ntlm2_response() { int i, j; int passlen = 0; +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#else MD4_CTX passcontext; +#endif unsigned char passdigest[16]; unsigned char *userdom; int userdomlen; @@ -327,9 +413,15 @@ void build_ntlm2_response() { } } +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + EVP_DigestInit_ex(mdctx, md4alg, NULL); + EVP_DigestUpdate(mdctx, unipasswd, passlen); + EVP_DigestFinal_ex(mdctx, passdigest, NULL); +#else MD4_Init (&passcontext); MD4_Update (&passcontext, unipasswd, passlen); MD4_Final (passdigest, &passcontext); +#endif if( args_info.verbose_flag ) { message("NTLM: MD4 of password is: "); @@ -339,12 +431,8 @@ void build_ntlm2_response() { } userdomlen = sizeof(unsigned char) * (strlen(args_info.user_arg) + strlen(domain)) * 2; - userdom = (unsigned char *)malloc(userdomlen); + userdom = (unsigned char *)alloca(userdomlen); memset(userdom, 0, userdomlen); - if (!userdom) { - message("Fatal Error in build_ntlm2_response, Malloc failed\n"); - exit(-1); - } userdomlen = 0; for (i = 0; i < strlen(args_info.user_arg); i++) { @@ -378,8 +466,6 @@ void build_ntlm2_response() { hmac_md5(userdom, userdomlen, passdigest, 16, userdomdigest); - free(userdom); - if( args_info.verbose_flag ) { message("HMAC_MD5 of userdom keyed with MD4 pass is: "); for( i = 0; i < 16; i++) diff --git a/ntlm.h b/ntlm.h index 01a6434..48a3384 100644 --- a/ntlm.h +++ b/ntlm.h @@ -18,6 +18,10 @@ */ /* ntlm.h */ + +#include + +void init_ntlm(); void build_type1(); int parse_type2(unsigned char *buf); void build_type3_response(); @@ -55,53 +59,53 @@ extern char ntlm_type3_buf[4096]; typedef struct { - unsigned short length; - unsigned short space; - unsigned long offset; + uint16_t length; + uint16_t space; + uint32_t offset; } security_buf_t; typedef struct { - unsigned char signature[8]; - unsigned long message_type; - unsigned long flags; + uint8_t signature[8]; + uint32_t message_type; + uint32_t flags; security_buf_t domain; security_buf_t workstation; } ntlm_type1; typedef struct { - unsigned char signature[8]; - unsigned long message_type; + uint8_t signature[8]; + uint32_t message_type; security_buf_t target_name; - unsigned long flags; - unsigned char challenge[8]; - unsigned long context1; - unsigned long context2; + uint32_t flags; + uint8_t challenge[8]; + uint32_t context1; + uint32_t context2; security_buf_t target_info; - unsigned char data_start; + uint8_t data_start; } ntlm_type2; typedef struct { - unsigned char signature[8]; - unsigned long message_type; + uint8_t signature[8]; + uint32_t message_type; security_buf_t LM_response; security_buf_t NTLM_response; security_buf_t domain; security_buf_t user; security_buf_t workstation; - unsigned char session[8]; - unsigned long flags; - unsigned char pad[8]; + uint8_t session[8]; + uint32_t flags; + uint8_t pad[8]; } ntlm_type3; typedef struct { - unsigned char digest[16]; - unsigned long signature; - unsigned long reserved; - unsigned long long timestamp; - unsigned char client_challenge[8]; - unsigned long unknown; - unsigned long data_start; + uint8_t digest[16]; + uint32_t signature; + uint32_t reserved; + uint64_t timestamp; + uint8_t client_challenge[8]; + uint32_t unknown; + uint32_t data_start; } blob; // vim:noexpandtab:ts=4 diff --git a/proxytunnel.c b/proxytunnel.c index e5127d8..a847223 100644 --- a/proxytunnel.c +++ b/proxytunnel.c @@ -29,11 +29,11 @@ #include #include #include +#include #include #include #include #include -#include #include "proxytunnel.h" #include "io.h" @@ -77,6 +77,10 @@ int tunnel_connect() { char service[6]; int sd; + if ( args_info.enforceipv4_flag ) + hints.ai_family = AF_INET; + else if ( args_info.enforceipv6_flag ) + hints.ai_family = AF_INET6; rc = snprintf( service, sizeof(service), "%d", args_info.proxyport_arg ); if( ( rc < 0 ) || ( rc >= sizeof(service) ) ) { /* this should never happen */ @@ -155,23 +159,110 @@ void closeall() { } } +/* Get the filled in sockaddr structure to the standalone daemon */ +void get_sa_serv(struct sockaddr **sa_serv_pp, socklen_t *sa_serv_len_p) +{ + static union { + struct sockaddr_in v4; + struct sockaddr_in6 v6; + } sa_serv; + int set_addr_result = 1; + + memset( &sa_serv, '\0', sizeof( sa_serv ) ); + + /* In case a standalone address has been specified ... */ + if ( args_info.standalone_addr_given ) { + /* ... and it looks like a IPv6 address ... */ + if ( strchr( args_info.standalone_addr, ':' ) ){ + /* ... set IPv6 address family and port, ... */ + sa_serv.v6.sin6_family = AF_INET6; + sa_serv.v6.sin6_port = htons( args_info.standalone_port ); + /* ... in case a standalone interface has been specified ... */ + if ( args_info.standalone_iface_given ) { + /* ... try to get and set the interface's index */ + if ( !(sa_serv.v6.sin6_scope_id = if_nametoindex(args_info.standalone_iface)) ) { + set_addr_result = -2; + } + } + /* If no error happened regarding the interface ... */ + if ( set_addr_result != -2 ) { + /* ... try to set the presumed IPv6 address. */ + set_addr_result = + inet_pton(AF_INET6, + args_info.standalone_addr, + &sa_serv.v6.sin6_addr); + } + /* ... otherwise (if it does not look like a IPv6 address) ... */ + } else { + /* ... set IPv4 address family and port, ... */ + sa_serv.v4.sin_family = AF_INET; + sa_serv.v4.sin_port = htons( args_info.standalone_port ); + /* ... try to set the presumed IPv4 address. */ + set_addr_result = + inet_pton(AF_INET, + args_info.standalone_addr, + &sa_serv.v4.sin_addr); + } + /* In case no standalone address has been specified ... */ + } else { + /* ... set IPv6 family, port and any address */ + sa_serv.v6.sin6_family = AF_INET6; + sa_serv.v6.sin6_port = htons( args_info.standalone_port ); + sa_serv.v6.sin6_addr = in6addr_any; + } + + /* Bail out on errors */ + switch (set_addr_result) { + case 0: + my_perror("Setting server socket IP address failed, possibly malformed"); + exit(1); + case -1: + my_perror("Setting server socket address family failed."); + exit(1); + case -2: + my_perror("Setting server socket interface failed, possibly mis-spelled"); + exit(1); + } + + /* Return pointer to sockaddr struct and its size */ + *sa_serv_pp = (struct sockaddr *)&sa_serv; + *sa_serv_len_p = sizeof( sa_serv ); + return; +} + +/* Log pid and IP address of client */ +void log_client(int pid, struct sockaddr_storage *ss_client_p) +{ + char buf[40]; + + inet_ntop(ss_client_p->ss_family, + ss_client_p->ss_family == AF_INET ? + (void *)&(((struct sockaddr_in *)ss_client_p)->sin_addr) : + (void *)&(((struct sockaddr_in6 *)ss_client_p)->sin6_addr), + buf, + sizeof(buf)); + message( "Started tunnel pid=%d for connection from %s", pid, buf ); + return; +} + /* Run as a standalone daemon */ void do_daemon() { int listen_sd; int one = 1; - struct sockaddr_in sa_serv; - struct sockaddr_in sa_cli; + struct sockaddr *sa_serv_p; + socklen_t sa_serv_len; + struct sockaddr_storage sa_cli; socklen_t client_len; int pid = 0; int sd_client; - char buf[80]; - unsigned char addr[4]; /* Socket descriptor */ int sd; - if ( ( listen_sd = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ) ) < 0 ) { + get_sa_serv(&sa_serv_p, &sa_serv_len); + + if ( ( listen_sd = socket( sa_serv_p->sa_family, SOCK_STREAM, IPPROTO_TCP ) ) < 0 ) { my_perror( "Server socket creation failed" ); exit(1); } @@ -181,12 +272,7 @@ void do_daemon() #endif /* SO_REUSEPORT */ setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); - memset( &sa_serv, '\0', sizeof( sa_serv ) ); - sa_serv.sin_family = AF_INET; - sa_serv.sin_addr.s_addr = htonl(INADDR_ANY); - sa_serv.sin_port = htons( args_info.standalone_arg ); - - if ( bind( listen_sd, (struct sockaddr * )&sa_serv, sizeof( struct sockaddr ) ) < 0) { + if ( bind( listen_sd, sa_serv_p, sa_serv_len ) < 0) { my_perror("Server socket bind failed"); exit(1); } @@ -238,6 +324,10 @@ void do_daemon() * we'll do it by default, can't hurt * * -- Maniac + * + * 2024/01/21: Not sure what makes up the workaround + * + * -- Sven */ client_len = sizeof( sa_cli ); @@ -253,6 +343,9 @@ void do_daemon() if ( ( pid = fork() ) < 0 ) { my_perror( "Cannot fork worker" ); } else if ( pid == 0 ) { + /* As worker, we do not need to listen for new connections */ + close(listen_sd); + read_fd = write_fd = sd_client; /* Create a stdin/out stream */ @@ -292,9 +385,7 @@ void do_daemon() exit( 0 ); } - memcpy( &addr, &sa_cli.sin_addr.s_addr, 4 ); - snprintf( (char *) buf, 16, "%u.%u.%u.%u", addr[0], addr[1], addr[2], addr[3] ); - message( "Started tunnel pid=%d for connection from %s", pid, buf ); + log_client(pid, &sa_cli); close( sd_client ); } } @@ -329,6 +420,11 @@ int main( int argc, char *argv[] ) { signal( SIGHUP, signal_handler ); + /* Initialize the NTLM module, if needed. */ + if (args_info.ntlm_flag) { + init_ntlm(); + } + /* If the usename is given, but password is not, prompt for it */ if( args_info.user_given && !args_info.pass_given ) { char *cp; diff --git a/proxytunnel.h b/proxytunnel.h index 74547f8..88a5471 100644 --- a/proxytunnel.h +++ b/proxytunnel.h @@ -30,8 +30,10 @@ void analyze_HTTP(PTSTREAM *pts); void proxy_protocol(PTSTREAM *pts); void closeall(); void do_daemon(); +#ifdef SETPROCTITLE void initsetproctitle(int argc, char *argv[]); void setproctitle(const char *fmt, ...); +#endif #if defined(__APPLE__) && defined(__MACH__) /* Don't include strlcat and strlcpy since they are provided as macros on OSX */ diff --git a/ptstream.c b/ptstream.c index 7f6a174..d7c6424 100644 --- a/ptstream.c +++ b/ptstream.c @@ -222,11 +222,10 @@ int check_cert_names(X509 *cert, char *peer_host) { for (i = 0; i < san_count; i++) { gn = sk_GENERAL_NAME_value(gen_names, i); if (gn->type == GEN_DNS && !(peer_host_is_ipv4 || peer_host_is_ipv6)) { -#ifdef OPENSSL11 +#if OPENSSL_VERSION_NUMBER >= 0x10100000L if (check_cert_valid_host((char*)ASN1_STRING_get0_data(gn->d.ia5), peer_host)) { #else if (check_cert_valid_host((char*)ASN1_STRING_data(gn->d.ia5), peer_host)) { - #endif return 1; } @@ -263,30 +262,33 @@ int stream_enable_ssl(PTSTREAM *pts, const char *proxy_arg) { X509* cert = NULL; int status; struct stat st_buf; +#ifndef DEFAULT_CA_FILE const char *ca_file = NULL; +#else + const char *ca_file = DEFAULT_CA_FILE; /* Default cert file from Makefile */ +#endif /* !DEFAULT_CA_FILE */ +#ifndef DEFAULT_CA_DIR const char *ca_dir = "/etc/ssl/certs/"; /* Default cert directory if none given */ +#else + const char *ca_dir = DEFAULT_CA_DIR; /* Default cert directory from Makefile */ +#endif /* !DEFAULT_CA_DIR */ long vresult; + const char *peer_arg = NULL; + size_t peer_arg_len; + char peer_arg_fmt[32]; char *peer_host = NULL; - char proxy_arg_fmt[32]; - size_t proxy_arg_len; /* Initialise the connection */ SSLeay_add_ssl_algorithms(); - if (args_info.enforcetls1_flag) { -#ifdef OPENSSL11 - meth = TLS_client_method(); +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + meth = TLS_client_method(); #else - meth = TLSv1_client_method(); + meth = SSLv23_client_method(); #endif - } else { - meth = SSLv23_client_method(); - } SSL_load_error_strings(); ctx = SSL_CTX_new (meth); - if (args_info.no_ssl3_flag) { - ssl_options |= SSL_OP_NO_SSLv3; - } + ssl_options |= SSL_OP_NO_SSLv3; SSL_CTX_set_options (ctx, ssl_options); if ( !args_info.no_check_cert_flag ) { @@ -308,19 +310,33 @@ int stream_enable_ssl(PTSTREAM *pts, const char *proxy_arg) { } } + /* If given, load client certificate (chain) and key */ + if ( args_info.clientcert_given && args_info.clientkey_given ) { + if ( 1 != SSL_CTX_use_certificate_chain_file(ctx, args_info.clientcert_arg) ) { + message("Error loading client certificate (chain) from %s\n", args_info.clientcert_arg); + goto fail; + } + if ( 1 != SSL_CTX_use_PrivateKey_file(ctx, args_info.clientkey_arg, SSL_FILETYPE_PEM) ) { + message("Error loading client key from %s, or key does not match certificate\n", args_info.clientkey_arg); + goto fail; + } + } + ssl = SSL_new (ctx); + if ( ssl == NULL ) { + message("SSL_new failed\n"); + goto fail; + } SSL_set_rfd (ssl, stream_get_incoming_fd(pts)); SSL_set_wfd (ssl, stream_get_outgoing_fd(pts)); /* Determine the host name we are connecting to */ - proxy_arg_len = strlen(proxy_arg); - if ((peer_host = malloc(proxy_arg_len + 1)) == NULL) { - message("Out of memory\n"); - goto fail; - } - snprintf( proxy_arg_fmt, sizeof(proxy_arg_fmt), proxy_arg[0] == '[' ? "[%%%zu[^]]]" : "%%%zu[^:]", proxy_arg_len - 1 ); - if ( sscanf( proxy_arg, proxy_arg_fmt, peer_host ) != 1 ) { + peer_arg = args_info.host_given ? args_info.host_arg : proxy_arg; + peer_arg_len = strlen(peer_arg); + peer_host = alloca(peer_arg_len + 1); + snprintf( peer_arg_fmt, sizeof(peer_arg_fmt), peer_arg[0] == '[' ? "[%%%zu[^]]]" : "%%%zu[^:]", peer_arg_len); + if ( sscanf( peer_arg, peer_arg_fmt, peer_host ) != 1 ) { goto fail; } @@ -329,12 +345,16 @@ int stream_enable_ssl(PTSTREAM *pts, const char *proxy_arg) { message( "Set SNI hostname to %s\n", peer_host); } res = SSL_set_tlsext_host_name(ssl, peer_host); - if (res < 0) { - message( "TLS SNI error, giving up: SSL_set_tlsext_host_name returned error message:\n %u\n", res ); - exit( 1 ); + if ( res != 1 ) { + message( "SSL_set_tlsext_host_name() failed for host name '%s'. " + "TLS SNI error, giving up\n", peer_host); + goto fail; } - SSL_connect (ssl); + if ( SSL_connect (ssl) <= 0) { + message( "SSL_connect failed\n"); + goto fail; + } if ( !args_info.no_check_cert_flag ) { /* Make sure peer presented a certificate */ @@ -357,7 +377,6 @@ int stream_enable_ssl(PTSTREAM *pts, const char *proxy_arg) { goto fail; } - free(peer_host); X509_free(cert); } @@ -375,9 +394,6 @@ fail: if (cert != NULL) { X509_free(cert); } - if (peer_host != NULL) { - free(peer_host); - } #endif /* USE_SSL */ exit(1); } diff --git a/ptstream.h b/ptstream.h index ee36af2..ccfd717 100644 --- a/ptstream.h +++ b/ptstream.h @@ -19,7 +19,9 @@ /* ptstream.h */ +#include #ifdef USE_SSL +#include #include #include #include