diff options
-rw-r--r-- | .gitlab-ci.yml | 21 | ||||
-rw-r--r-- | .travis.yml | 35 | ||||
-rw-r--r-- | PKGBUILD | 4 | ||||
-rw-r--r-- | README | 2 | ||||
-rw-r--r-- | README.md | 3 | ||||
-rw-r--r-- | configure.ac | 13 | ||||
-rw-r--r-- | src/Makefile.am | 11 | ||||
-rw-r--r-- | src/challenge.c | 51 | ||||
-rw-r--r-- | src/challenge.h | 41 | ||||
-rw-r--r-- | src/options.c | 55 | ||||
-rw-r--r-- | src/options.h | 10 | ||||
-rw-r--r-- | src/pconfig.h | 2 | ||||
-rw-r--r-- | src/pkt.c | 33 | ||||
-rw-r--r-- | src/pkt.h | 3 | ||||
-rw-r--r-- | src/ptunnel.c | 40 | ||||
-rw-r--r-- | src/utils.c | 2 |
16 files changed, 236 insertions, 90 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8b29b77..7c51609 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -12,12 +12,15 @@ before_script: - test ! -r /etc/arch-release || pacman -Sy archlinux-keyring --noconfirm - test ! -r /etc/arch-release || pacman -Syu --noconfirm - test ! -r /etc/arch-release || pacman -S --noconfirm binutils gcc base-devel git + - mkdir -p ./deploy/gcc ./deploy/i686-w64-mingw32-winpcap ./deploy/i686-w64-mingw32-npcap ./deploy/clang ./deploy/android28-clang build: script: - autoreconf -fi - - ./configure --prefix=/ - - make install CFLAGS='-Werror' DESTDIR="$(realpath ./deploy)" V=s + - ./configure --enable-option-checking=fatal --prefix=/ + - make install CFLAGS='-Werror' DESTDIR="$(realpath ./deploy/gcc)" V=s + - make dist + - cp ptunnel-ng-*.tar.gz ./deploy/ stage: build artifacts: paths: @@ -47,8 +50,10 @@ build-archlinux: build-mingw: script: - autoreconf -fi - - ./configure --prefix=/ --host=i686-w64-mingw32 - - make install CFLAGS='-Werror' DESTDIR="$(realpath ./deploy)" V=s + - ./configure --enable-option-checking=fatal --prefix=/ --host=i686-w64-mingw32 + - make install CFLAGS='-Werror' DESTDIR="$(realpath ./deploy/i686-w64-mingw32-winpcap)" V=s + - ./configure --enable-option-checking=fatal --prefix=/ --host=i686-w64-mingw32 --enable-npcap + - make install CFLAGS='-Werror' DESTDIR="$(realpath ./deploy/i686-w64-mingw32-npcap)" V=s stage: build artifacts: paths: @@ -59,8 +64,8 @@ build-mingw: build-clang: script: - autoreconf -fi - - CC=clang ./configure --prefix=/ - - make install CFLAGS='-Werror -Wno-error=for-loop-analysis' DESTDIR="$(realpath ./deploy)" V=s + - CC=clang ./configure --enable-option-checking=fatal --prefix=/ + - make install CFLAGS='-Werror' DESTDIR="$(realpath ./deploy/clang)" V=s stage: build artifacts: paths: @@ -79,8 +84,8 @@ build-android: - test -d 'android-ndk-r19' || unzip -q 'android-ndk-r19-linux-x86_64.zip' - cd .. - autoreconf -fi - - CC=aarch64-linux-android28-clang PATH="${PATH}:$(realpath ./vendor/android-ndk-r19/toolchains/llvm/prebuilt/linux-x86_64/bin)" ./configure --host=aarch64-linux-android - - PATH="${PATH}:$(realpath ./vendor/android-ndk-r19/toolchains/llvm/prebuilt/linux-x86_64/bin)" make install CFLAGS='-Werror -Wno-error=for-loop-analysis' DESTDIR="$(realpath ./deploy)" V=s + - CC=aarch64-linux-android28-clang PATH="${PATH}:$(realpath ./vendor/android-ndk-r19/toolchains/llvm/prebuilt/linux-x86_64/bin)" ./configure --enable-option-checking=fatal --host=aarch64-linux-android + - PATH="${PATH}:$(realpath ./vendor/android-ndk-r19/toolchains/llvm/prebuilt/linux-x86_64/bin)" make install CFLAGS='-Werror' DESTDIR="$(realpath ./deploy/android28-clang)" V=s stage: build artifacts: paths: diff --git a/.travis.yml b/.travis.yml index 20c9c90..630d2bb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,34 +4,47 @@ before_install: - sudo apt-get install -y git debhelper dpkg-dev build-essential fakeroot flawfinder wget unzip realpath - sudo apt-get install -y libpcap-dev libselinux1-dev - sudo apt-get install -y binutils-mingw-w64-i686 gcc-mingw-w64 mingw-w64-i686-dev mingw-w64-common clang +- sudo apt-get install -y binutils-mingw-w64-x86-64 mingw-w64-x86-64-dev - sudo apt-get install -y autoconf automake dh-autoreconf after_failure: - cat config.log script: -- mkdir -p ./deploy +- mkdir -p ./deploy ./deploy/gcc ./deploy/mingw-w64-i386-winpcap ./deploy/mingw-w64-i386-npcap ./deploy/mingw-w64-x86_64-winpcap ./deploy/mingw-w64-x86_64-npcap ./deploy/clang ./deploy/android28-clang # default gcc build - autoreconf -fi -- ./configure --prefix=/ -- make install CFLAGS='-Werror' DESTDIR="$(realpath ./deploy)" V=s +- ./configure --enable-option-checking=fatal --prefix=/ +- make install CFLAGS='-Werror' DESTDIR="$(realpath ./deploy/gcc)" V=s - make clean # debian build - dpkg-buildpackage -b -us -uc - make clean -# mingw-w64 build -- CC=i686-w64-mingw32-gcc ./configure --prefix=/ --host=i686-w64-mingw32 -- make install CFLAGS='-Werror' DESTDIR="$(realpath ./deploy)" V=s +# mingw-w64-i386 build (WinPcap) +- CC=i686-w64-mingw32-gcc ./configure --enable-option-checking=fatal --prefix=/ --host=i686-w64-mingw32 +- make install CFLAGS='-Werror' DESTDIR="$(realpath ./deploy/mingw-w64-i386-winpcap)" V=s +- make clean +# mingw-w64-i386 build (Npcap) +- CC=i686-w64-mingw32-gcc ./configure --enable-option-checking=fatal --prefix=/ --host=i686-w64-mingw32 --enable-npcap +- make install CFLAGS='-Werror' DESTDIR="$(realpath ./deploy/mingw-w64-i386-npcap)" V=s +- make clean +# mingw-w64-x86_64 build (WinPcap) +- CC=x86_64-w64-mingw32-gcc ./configure --enable-option-checking=fatal --prefix=/ --host=x86_64-w64-mingw32 +- make install CFLAGS='-Werror' DESTDIR="$(realpath ./deploy/mingw-w64-x86_64-winpcap)" V=s +- make clean +# mingw-w64-x86_64 build (Npcap) +- CC=x86_64-w64-mingw32-gcc ./configure --enable-option-checking=fatal --prefix=/ --host=x86_64-w64-mingw32 --enable-npcap +- make install CFLAGS='-Werror' DESTDIR="$(realpath ./deploy/mingw-w64-x86_64-npcap)" V=s - make clean # clang build -- CC=clang ./configure --prefix=/ -- make install CFLAGS='-Werror -Wno-error=for-loop-analysis' DESTDIR="$(realpath ./deploy)" V=s +- CC=clang ./configure --enable-option-checking=fatal --prefix=/ +- make install CFLAGS='-Werror -Wno-error=for-loop-analysis' DESTDIR="$(realpath ./deploy/clang)" V=s - make clean # android build - mkdir -p vendor && cd vendor - test -r 'android-ndk-r19-linux-x86_64.zip' || wget --progress=dot:mega 'https://dl.google.com/android/repository/android-ndk-r19-linux-x86_64.zip' - test -d 'android-ndk-r19' || unzip -q 'android-ndk-r19-linux-x86_64.zip' - cd .. -- CC=aarch64-linux-android28-clang PATH="${PATH}:$(realpath ./vendor/android-ndk-r19/toolchains/llvm/prebuilt/linux-x86_64/bin)" ./configure --host=aarch64-linux-android -- PATH="${PATH}:$(realpath ./vendor/android-ndk-r19/toolchains/llvm/prebuilt/linux-x86_64/bin)" make install CFLAGS='-Werror -Wno-error=for-loop-analysis' DESTDIR="$(realpath ./deploy)" V=s +- CC=aarch64-linux-android28-clang PATH="${PATH}:$(realpath ./vendor/android-ndk-r19/toolchains/llvm/prebuilt/linux-x86_64/bin)" ./configure --enable-option-checking=fatal --host=aarch64-linux-android +- PATH="${PATH}:$(realpath ./vendor/android-ndk-r19/toolchains/llvm/prebuilt/linux-x86_64/bin)" make install CFLAGS='-Werror -Wno-error=for-loop-analysis' DESTDIR="$(realpath ./deploy/android28-clang)" V=s # archlinux build # see: https://wiki.archlinux.org/index.php/Install_from_existing_Linux#Method_A:_Using_the_bootstrap_image_(recommended) #- wget --progress=dot:mega 'https://ftp.fau.de/archlinux/iso/2019.02.01/archlinux-bootstrap-2019.02.01-x86_64.tar.gz' @@ -58,6 +71,6 @@ addons: project: name: lnslbrty/ptunnel-ng notification_email: matzeton@googlemail.com - build_command_prepend: autoreconf -i && ./configure + build_command_prepend: autoreconf -i && ./configure --enable-option-checking=fatal build_command: make branch_pattern: coverity_scan @@ -1,5 +1,5 @@ pkgname="ptunnel-ng" -pkgver=1.32 +pkgver=1.42 pkgrel=1 pkgdesc="A TCP forwarder and proxy used for ICMP/UDP tunneling without creating tun devices. (Ping Tunnel, ICMP Echo Tunnel, UDP Tunnel)" arch=('i686' 'x86_64') @@ -8,7 +8,7 @@ license=('BSD-3') makedepends=('git') provides=("ptunnel-ng=${pkgver}") source=("https://github.com/lnslbrty/ptunnel-ng/archive/v${pkgver}.tar.gz") -md5sums=('cbb9a17ed0ac728170e03f4fa618a617') +md5sums=('b7741527a7833bc06130ea67502ae21a') build() { cd "${srcdir}/${pkgname}-${pkgver}" @@ -123,7 +123,7 @@ installed. TODOs ----- -- challenge response: switch from md5 to sha-512 +- challenge response: switch from md5 to sha-512 (WiP) - packet obfuscation - encryption (metadata + payload) @@ -1,6 +1,7 @@ [](https://travis-ci.org/lnslbrty/ptunnel-ng) [](https://gitlab.com/lnslbrty/ptunnel-ng) [](https://scan.coverity.com/projects/14737) +[](https://lgtm.com/projects/g/lnslbrty/ptunnel-ng/alerts/) [](https://www.codacy.com/app/lnslbrty/ptunnel-ng?utm_source=github.com&utm_medium=referral&utm_content=lnslbrty/ptunnel-ng&utm_campaign=Badge_Grade) [](https://github.com/lnslbrty/ptunnel-ng/issues) [](https://github.com/lnslbrty/ptunnel-ng/blob/master/COPYING) @@ -140,7 +141,7 @@ installed. ## TODOs ``` -- challenge response: switch from md5 to sha-512 +- challenge response: switch from md5 to sha-512 (WiP) - packet obfuscation - encryption (metadata + payload) ``` diff --git a/configure.ac b/configure.ac index 5a6af52..457497f 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ AC_PREREQ(2.69) -AC_INIT([ptunnel-ng], [1.41], [], [], []) +AC_INIT([ptunnel-ng], [1.42], [], [], []) AC_CONFIG_SRCDIR([src/config.h.in]) AC_CONFIG_FILES([Makefile src/Makefile]) @@ -128,6 +128,16 @@ case ${pcap_enabled} in *) AC_MSG_ERROR([Unknown option \`${pcap_enabled}\` for --disable-pcap]) ;; esac +dnl `--enable-npcap`: Enable npcap interface (Windows only!) +AC_ARG_ENABLE([npcap], + [AS_HELP_STRING([--enable-npcap], [Enable npcap support. (Windows only; default: disabled)])],[npcap_enabled=yes],) +npcap_enabled=$(echo ${npcap_enabled}) +case ${npcap_enabled} in + 1|y|yes) pcap_enabled=yes ;; + ''|0|n|no) pcap_enabled= ;; + *) AC_MSG_ERROR([Unknown option \`${npcap_enabled}\` for --enable-npcap]) ;; +esac + dnl `--disable-selinux`: Enabled if found. AC_ARG_ENABLE([selinux], [AS_HELP_STRING([--disable-selinux], [Disable SELINUX support. (default: enabled if found)])],,[selinux_enabled=yes]) @@ -203,6 +213,7 @@ AC_SEARCH_LIBS([__android_log_vprint], [log],,,) dnl Set automake conf vars AM_CONDITIONAL([HAVE_PCAP], [test x"${pcap_enabled}" = xyes]) +AM_CONDITIONAL([HAVE_NPCAP], [test x"${npcap_enabled}" = xyes]) AM_CONDITIONAL([HAVE_SELINUX], [test x"${selinux_enabled}" = xyes]) AM_CONDITIONAL([IS_WINDOWS], [test x"${use_msw}" = xyes]) AM_CONDITIONAL([HAVE_ICMPFILTER], [test x"${with_icmp_filter}" = xyes]) diff --git a/src/Makefile.am b/src/Makefile.am index 8d4787a..552c894 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -36,14 +36,17 @@ ptunnel_ng_SOURCES = \ ptunnel.c if IS_WINDOWS -wpcap_DEF = $(abs_srcdir)/win32/WPCAP.DEF -wpcap_IMP = $(abs_srcdir)/win32/libwpcap_implib.a -ptunnel_ng_CFLAGS += -I$(abs_srcdir)/win32/includes +wpcap_DEF = $(srcdir)/win32/WPCAP.DEF +wpcap_IMP = $(srcdir)/win32/libwpcap_implib.a +ptunnel_ng_CFLAGS += -I$(srcdir)/win32/includes -DHAVE_PCAP=1 +if HAVE_NPCAP +ptunnel_ng_CFLAGS += -DHAVE_NPCAP=1 +endif ptunnel_ng_LDADD += $(wpcap_IMP) ptunnel_ng_SOURCES += $(wpcap_DEF) $(wpcap_IMP): - @DLLTOOL@ -k -l $(wpcap_IMP) --def $(wpcap_DEF) + @DLLTOOL@ -k -y $(wpcap_IMP) --def $(wpcap_DEF) CLEANFILES += $(wpcap_IMP) EXTTRA_DIST += $(wpcap_DEF) diff --git a/src/challenge.c b/src/challenge.c index f269313..f0b02ad 100644 --- a/src/challenge.c +++ b/src/challenge.c @@ -46,6 +46,7 @@ #include <stdlib.h> #include <string.h> #include <sys/time.h> +#include <assert.h> #include "challenge.h" #include "options.h" @@ -55,48 +56,66 @@ /* generate_challenge: Generates a random challenge, incorporating the current * local timestamp to avoid replay attacks. */ -challenge_t* generate_challenge(void) { +challenge_t *generate_challenge(void) { struct timeval tt; challenge_t *c; int i; c = (challenge_t *) calloc(1, sizeof(challenge_t)); + assert(c != NULL); gettimeofday(&tt, 0); - c->sec = tt.tv_sec; - c->usec_rnd = tt.tv_usec + pt_random(); + c->plain.sec = tt.tv_sec; + c->plain.usec_rnd = tt.tv_usec + pt_random(); for (i=0;i<6;i++) - c->random[i] = pt_random(); + c->plain.random[i] = pt_random(); return c; } -/* generate_response: Generates a response to the given challenge. The response +/* generate_response_md5: Generates a response to the given challenge. The response * is generated by combining the concatenating the challenge data with the * md5 digest of the password, and then calculating the MD5 digest of the * entire buffer. The result is stored in the passed-in challenge, overwriting * the challenge data. */ -void generate_response(challenge_t *challenge) { - md5_byte_t buf[sizeof(challenge_t)+kMD5_digest_size]; +void generate_response_md5(challenge_plain_t *plain, challenge_digest_t *digest) { + md5_byte_t buf[sizeof(*plain) + kMD5_digest_size]; md5_state_t state; - memcpy(buf, challenge, sizeof(challenge_t)); - memcpy(&buf[sizeof(challenge_t)], opts.password_digest, kMD5_digest_size); - memset(challenge, 0, sizeof(challenge_t)); + digest->hash_type = HT_MD5; + memcpy(buf, plain, sizeof(*plain)); + memcpy(&buf[sizeof(*plain)], opts.md5_password_digest, kMD5_digest_size); + memset(plain, 0, sizeof(*plain)); + md5_init(&state); - md5_append(&state, buf, sizeof(challenge_t)+kMD5_digest_size); - md5_finish(&state, (md5_byte_t*)challenge); + md5_append(&state, buf, sizeof(*plain) + kMD5_digest_size); + md5_finish(&state, (md5_byte_t *) &digest->md5[0]); } -/* validate_challenge: Checks whether a given response matches the expected +/* validate_challenge_md5: Checks whether a given response matches the expected * response, returning 1 if validation succeeded, and 0 otherwise. Note that * overwriting the local challenge with the challenge result is not a problem, * as the data will not be used again anyway (authentication either succeeds, * or the connection is closed down). */ -int validate_challenge(challenge_t *local, challenge_t *remote) { - generate_response(local); - if (memcmp(local, remote, sizeof(challenge_t)) == 0) +int validate_challenge_md5(challenge_t *local, challenge_digest_t *remote) { + generate_response_md5(&local->plain, &local->digest); + if (remote->hash_type == HT_MD5 && + memcmp(&local->digest.md5[0], &remote->md5[0], sizeof(local->digest.md5)) == 0) + { return 1; + } return 0; } + +#ifdef ENABLE_SHA512 +void generate_response_sha512(challenge_t *challenge) +{ + /* TODO: Implement me! */ +} + +int validate_challenge_sha512(challenge_t *local, challenge_t *remote) +{ + /* TODO: Implement me! */ +} +#endif /* ENABLE_SHA512 */ diff --git a/src/challenge.h b/src/challenge.h index 18495cf..203e420 100644 --- a/src/challenge.h +++ b/src/challenge.h @@ -46,23 +46,50 @@ #ifndef CHALLENGE_H #define CHALLENGE_H 1 +#include "pconfig.h" + #include <stdint.h> +#ifdef ENABLE_SHA512 +#include <openssl/sha.h> +#endif -/** challenge_t: This structure contains the pseudo-random challenge used for - * authentication. - */ -typedef struct challenge_t { +#define HT_MD5 0x1 +#define HT_SHA512 0x2 + + +typedef struct challenge_plain_t { /** tv_sec as returned by gettimeofday */ uint32_t sec; /** tv_usec as returned by gettimeofday + random value */ uint32_t usec_rnd; /** random values */ uint32_t random[6]; +} __attribute__ ((packed)) challenge_plain_t; + +typedef struct challenge_digest_t { + uint8_t hash_type; + union { + unsigned char md5[kMD5_digest_size]; + unsigned char sha512[kSHA512_digest_size]; + }; +} __attribute__ ((packed)) challenge_digest_t; + +/** challenge_t: This structure contains the pseudo-random challenge used for + * authentication. If OpenSSL is available SHA512 will be used per default. + */ +typedef struct challenge_t { + challenge_plain_t plain; + challenge_digest_t digest; } __attribute__ ((packed)) challenge_t; +challenge_t *generate_challenge(void); + +void generate_response_md5(challenge_plain_t *plain, challenge_digest_t *digest); +int validate_challenge_md5(challenge_t *local, challenge_digest_t *remote); -challenge_t* generate_challenge(void); -void generate_response(challenge_t *challenge); -int validate_challenge(challenge_t *local, challenge_t *remote); +#ifdef ENABLE_SHA512 +void generate_response_sha512(challenge_plain_t *plain, challenge_digest_t *digest); +int validate_challenge_sha512(challenge_t *local, challenge_digest_t *remote); +#endif #endif diff --git a/src/options.c b/src/options.c index ffb5339..10838ad 100644 --- a/src/options.c +++ b/src/options.c @@ -41,6 +41,9 @@ #ifdef WIN32 #include <ws2tcpip.h> #endif +#ifdef ENABLE_SHA512 +#include <openssl/sha.h> +#endif #ifdef HAVE_CONFIG_H #include "config.h" @@ -107,7 +110,7 @@ static const struct option_usage usage[] = { "The special level 5 (or higher) includes xfer logging (lots of output)\n" }, /** --libpcap */ - {"interface", 0, OPT_STR, {.str = "eth0"}, + {"interface", 0, OPT_STR, {.str = NULL}, #ifndef HAVE_PCAP "(Not available on this platform.)\n" #endif @@ -169,6 +172,22 @@ static const struct option_usage usage[] = { "Tune the number of empty pings to send with each explicit acknowledgement.\n" "Empty pings can compensate for ICMP sequence number inspection.\n" }, + /** --force-md5 */ + {"force-md5", 0, OPT_BOOL, {.num = 0}, + "Force MD5 as challenge response checksum generator.\n" +#ifndef ENABLE_SHA512 + "This is the default for this configuration.\n" +#endif + }, + /** --force-sha512 */ + {"force-sha512", 0, OPT_BOOL, {.num = 0}, + "Force SHA512 as challenge response checksum generator.\n" +#ifdef ENABLE_SHA512 + "This is the default for this configuration.\n" +#else + "SHA512 is not available for this configuration.\n" +#endif + }, /** --daemon */ {"pidfile", 0, OPT_STR, {.str = "/run/ptunnel.pid"}, #ifdef WIN32 @@ -237,6 +256,8 @@ static struct option long_options[] = { {"resend-interval", required_argument, 0, 't'}, {"payload-size", required_argument, 0, 'y'}, {"empty-pings", required_argument, 0, 'E'}, + {"force-md5", no_argument, &opts.force_md5, 1}, + {"force-sha512", no_argument, &opts.force_sha512, 1}, {"daemon", optional_argument, 0, 'd'}, {"syslog", no_argument, 0, 'S'}, {"user", optional_argument, 0, 'u'}, @@ -250,8 +271,11 @@ static struct option long_options[] = { static const void *get_default_optval(enum option_type opttype, const char *optname) { for (unsigned i = 0; i < ARRAY_SIZE(long_options); ++i) { - if (strncmp(long_options[i].name, optname, BUFSIZ /* not optimal */) == 0) { - assert(usage[i].otype == opttype); + if (strncmp(long_options[i].name, optname, BUFSIZ /* not optimal */) == 0 && + strlen(long_options[i].name) == strlen(optname)) + { + assert(usage[i].otype == opttype && + (usage[i].otype != OPT_STR || usage[i].str)); return &usage[i].str; } } @@ -274,9 +298,6 @@ static void set_options_defaults(void) { opts.given_dst_port = *(uint32_t *) get_default_optval(OPT_DEC32, "remote-port"); opts.max_tunnels = *(uint32_t *) get_default_optval(OPT_DEC32, "connections"); opts.log_level = *(int *) get_default_optval(OPT_DEC32, "verbosity"); -#ifdef HAVE_PCAP - opts.pcap_device = strdup(*(char **)get_default_optval(OPT_STR, "libpcap")); -#endif opts.log_path = strdup(*(char **)get_default_optval(OPT_STR, "logfile")); opts.log_file = stdout; opts.print_stats = *(int *) get_default_optval(OPT_BOOL, "statistics"); @@ -421,7 +442,7 @@ int parse_options(int argc, char **argv) { * since you have to pass long options as '--option=value'. Commonly used * '--option value' is *NOT* allowed for some libc implementations. */ - c = getopt_long(argc, argv, "m:p:l:r::R::c:v:L::o::sP:d::Su::g::C::e::w:a:t:y:E:h", &long_options[0], &oidx); + c = getopt_long(argc, argv, "m:p:l:r::R::c:v:L:o::sP:d::Su::g::C::e::w:a:t:y:E:h", &long_options[0], &oidx); if (c == -1) break; switch (c) { @@ -498,13 +519,17 @@ int parse_options(int argc, char **argv) { if (opts.password) free(opts.password); opts.password = strdup(optarg); - pt_log(kLog_debug, "Password set - unauthenicated connections will be refused.\n"); - // Compute the password digest + pt_log(kLog_debug, "%s\n", "Password set - unauthenicated connections will be refused."); + /* Compute the md5 password digest */ md5_init(&state); md5_append(&state, (md5_byte_t*)optarg, strnlen(opts.password, BUFSIZ /* not optimal */)); - md5_finish(&state, &opts.password_digest[0]); + md5_finish(&state, &opts.md5_password_digest[0]); // Hide the password in process listing memset(optarg, '*', strnlen(optarg, BUFSIZ /* not optimal */)); +#ifdef ENABLE_SHA512 + pt_log(kLog_debug, "%s\n", "Password set - sha512 authentication enabled."); + SHA512(optarg, strnlen(opts.password, BUFSIZ /* not optimal */), &opts.sha512_password_digest[0]); +#endif break; #ifndef WIN32 case 'd': @@ -609,6 +634,16 @@ int parse_options(int argc, char **argv) { exit(1); } +#if ENABLE_SHA512 + if (opts.force_md5) { + pt_log(kLog_error, "%s\n", "You are forcing md5 but sha512 is available."); + } +#else + if (opts.force_sha512) { + pt_log(kLog_error, "%s\n", "You are forcing sha512 but it isn't available."); + } +#endif + if (opts.given_proxy_hostname) { if ((ret = host_to_addr(opts.given_proxy_hostname, &opts.given_proxy_ip)) != 0) { pt_log(kLog_error, "Failed to look up %s as destination address: %s\n", diff --git a/src/options.h b/src/options.h index 9c2ca3f..5551426 100644 --- a/src/options.h +++ b/src/options.h @@ -75,6 +75,10 @@ struct options { /** Device to capture packets from */ char *pcap_device; #endif + /** Force MD5 based challenge response. */ + int force_md5; + /** Force SHA512 based challenge response. */ + int force_sha512; /** List all available pcap devices and exit */ int list_pcap_devices; /** Usually stdout, but can be altered by the user */ @@ -84,8 +88,10 @@ struct options { int print_stats; /** Password (must be the same on proxy and client for authentica tion to succeed) */ char *password; - /** MD5 digest of challenge+password */ - md5_byte_t password_digest[kMD5_digest_size]; + /** MD5 digest of password */ + md5_byte_t md5_password_digest[kMD5_digest_size]; + /** SHA512 digest of password */ + unsigned char sha512_password_digest[kSHA512_digest_size]; /** use UDP instead of ICMP */ int udp; /** unpriviledged mode */ diff --git a/src/pconfig.h b/src/pconfig.h index e13f7ee..5c642ee 100644 --- a/src/pconfig.h +++ b/src/pconfig.h @@ -88,6 +88,8 @@ enum { kAutomatic_close_timeout = 60, // Seconds! /** size of md5 digest in bytes */ kMD5_digest_size = 16, + /** size of sha512 digest in bytes */ + kSHA512_digest_size = 64, /** These constants are used to indicate the protocol state. The protocol * works as follows: * - The identifier is used by both the proxy and the forwarder @@ -76,7 +76,7 @@ void handle_packet(char *buf, unsigned bytes, int is_pcap, struct sockaddr_in *a if (bytes < sizeof(icmp_echo_packet_t)+sizeof(ping_tunnel_pkt_t)) pt_log(kLog_verbose, "Skipping this packet - too short. " - "Expect: %d+%d = %d ; Got: %d\n", + "Expect: %lu+%lu = %lu ; Got: %u\n", sizeof(icmp_echo_packet_t), sizeof(ping_tunnel_pkt_t), sizeof(icmp_echo_packet_t) + @@ -176,9 +176,9 @@ void handle_packet(char *buf, unsigned bytes, int is_pcap, struct sockaddr_in *a init_state = kProto_data; cur = (proxy_desc_t *) create_and_insert_proxy_desc(pt_pkt->id_no, pkt->identifier, 0, - addr, pt_pkt->dst_ip, - ntohl(pt_pkt->dst_port), - init_state, kProxy_flag); + addr, pt_pkt->dst_ip, + ntohl(pt_pkt->dst_port), + init_state, kProxy_flag); if (!cur) { /* if failed, abort. Logging is done in create_insert_proxy_desc */ pt_log(kLog_error, "Failed to create proxy descriptor!\n"); @@ -236,7 +236,7 @@ void handle_packet(char *buf, unsigned bytes, int is_pcap, struct sockaddr_in *a return; } pt_log(kLog_debug, "Got authentication challenge - sending response\n"); - generate_response(challenge); + generate_response_md5(&challenge->plain, &challenge->digest); queue_packet(icmp_sock, cur->pkt_type, (char*)challenge, sizeof(challenge_t), cur->id_no, cur->icmp_id, &cur->my_seq, cur->send_ring, &cur->send_idx, @@ -254,8 +254,8 @@ void handle_packet(char *buf, unsigned bytes, int is_pcap, struct sockaddr_in *a /* If proxy: Handle client's response to challenge */ else if (type_flag == proxy_flag) { pt_log(kLog_debug, "Received remote challenge response.\n"); - if (validate_challenge(cur->challenge, challenge) || - cur->authenticated) + if (validate_challenge_md5(cur->challenge, &challenge->digest) || + cur->authenticated) { pt_log(kLog_verbose, "Remote end authenticated successfully.\n"); handle_extended_options(cur); @@ -432,14 +432,13 @@ void handle_data(icmp_echo_packet_t *pkt, int total_len, forward_desc_t *ring[], } } -void handle_extended_options(void *vcur) +void handle_extended_options(proxy_desc_t *cur) { - proxy_desc_t *cur = (proxy_desc_t *)vcur; if (cur->extended_options[0] > 0) { if (cur->extended_options[0] > cur->window_size) { size_t extend = cur->extended_options[0] - cur->window_size; - cur->send_ring = realloc(cur->send_ring, cur->extended_options[0] * sizeof(icmp_desc_t)); - cur->recv_ring = realloc(cur->recv_ring, cur->extended_options[0] * sizeof(forward_desc_t *)); + cur->send_ring = (icmp_desc_t *) realloc(cur->send_ring, cur->extended_options[0] * sizeof(icmp_desc_t)); + cur->recv_ring = (forward_desc_t **) realloc(cur->recv_ring, cur->extended_options[0] * sizeof(forward_desc_t *)); memset(cur->send_ring + cur->window_size, 0, extend * sizeof(icmp_desc_t)); memset(cur->recv_ring + cur->window_size, 0, extend * sizeof(forward_desc_t *)); } @@ -474,14 +473,14 @@ void handle_ack(uint16_t seq_no, icmp_desc_t ring[], int *packets_awaiting_ack, pt_log(kLog_debug, "Received ack for only seq %d\n", seq_no); pt_pkt = (ping_tunnel_pkt_t*)ring[i].pkt->data; /* WARNING: We make the dangerous assumption here that packets arrive in order! */ - *remote_ack = (uint16_t)ntohl(pt_pkt->ack); + *remote_ack = (uint16_t) ntohl(pt_pkt->ack); free(ring[i].pkt); ring[i].pkt = 0; ring[i].pkt_len = 0; (*packets_awaiting_ack)--; if (i == *first_ack) { - for (j=1;j<window_size;j++) { - k = (i+j)%window_size; + for (j = 1; j < window_size; j += 2) { + k = (i + j) % window_size; if (ring[k].pkt) { *first_ack = k; break; @@ -489,7 +488,6 @@ void handle_ack(uint16_t seq_no, icmp_desc_t ring[], int *packets_awaiting_ack, /* we have looped through everything */ if (k == i) *first_ack = insert_idx; - j++; } } return; @@ -525,7 +523,6 @@ void handle_ack(uint16_t seq_no, icmp_desc_t ring[], int *packets_awaiting_ack, } } } -/* else - * pt_log(kLog_verbose, "Dropping superfluous acknowledgement (no outstanding packets needing ack.)\n"); - */ + else + pt_log(kLog_verbose, "Dropping superfluous acknowledgement (no outstanding packets needing ack.)\n"); } @@ -133,6 +133,7 @@ typedef struct { typedef struct forward_desc_t forward_desc_t; typedef struct icmp_desc_t icmp_desc_t; +typedef struct proxy_desc_t proxy_desc_t; void handle_packet(char *buf, unsigned bytes, int is_pcap, struct sockaddr_in *addr, int icmp_sock); @@ -140,7 +141,7 @@ void handle_packet(char *buf, unsigned bytes, int is_pcap, struct sockaddr_in *a void handle_data(icmp_echo_packet_t *pkt, int total_len, forward_desc_t **ring, int *await_send, int *insert_idx, uint16_t *next_expected_seq, void *vcur, uint16_t window_size); -void handle_extended_options(void *vcur); +void handle_extended_options(proxy_desc_t *cur); void handle_ack(uint16_t seq_no, icmp_desc_t *ring, int *packets_awaiting_ack, int one_ack_only, int insert_idx, int *first_ack, diff --git a/src/ptunnel.c b/src/ptunnel.c index d70edce..9af8db7 100644 --- a/src/ptunnel.c +++ b/src/ptunnel.c @@ -54,6 +54,7 @@ #endif #ifdef WIN32 +#include <tchar.h> #include <winsock2.h> /* Map errno (which Winsock doesn't use) to GetLastError; include the code in the strerror */ #ifdef errno @@ -75,6 +76,25 @@ static char * print_last_windows_error() { #define strerror(x) print_last_windows_error() #endif /* WIN32 */ +#ifdef HAVE_NPCAP +static BOOL LoadNpcapDlls() +{ + TCHAR npcap_dir[512]; + UINT len; + len = GetSystemDirectory(npcap_dir, 480); + if (!len) { + pt_log(kLog_error, "Error in GetSystemDirectory: %x", GetLastError()); + return FALSE; + } + _tcscat_s(npcap_dir, 512, _T("\\Npcap")); + if (SetDllDirectory(npcap_dir) == 0) { + pt_log(kLog_error, "Error in SetDllDirectory: %x", GetLastError()); + return FALSE; + } + return TRUE; +} +#endif + /* globals */ /** Lock protecting the chain of connections */ pthread_mutex_t chain_lock; @@ -150,7 +170,13 @@ int main(int argc, char *argv[]) { } #endif /* WIN32 */ - memset(opts.password_digest, 0, kMD5_digest_size); +#ifdef HAVE_NPCAP + if (!LoadNpcapDlls()) + return -1; +#endif + + memset(opts.md5_password_digest, 0, kMD5_digest_size); + memset(opts.sha512_password_digest, 0, kSHA512_digest_size); /* The seq_expiry_tbl is used to prevent the remote ends from prematurely * re-using a sequence number. @@ -181,9 +207,9 @@ int main(int argc, char *argv[]) { } #ifdef WIN32 if (!opts.pcap && !opts.udp) { - pt_log(kLog_info, "Running ptunnel-ng on Windows in ICMP mode without WinPcap enabled is not supported and may not work!\n"); - pt_log(kLog_info, "If you encounter problems, install WinPCAP from:\n"); - pt_log(kLog_info, "https://www.winpcap.org/install/default.htm or for WIN10: https://nmap.org/npcap/windows-10.html\n"); + pt_log(kLog_info, "Running ptunnel-ng on Windows in ICMP mode without WinPcap/Npcap enabled is not supported and may not work!\n"); + pt_log(kLog_info, "If you encounter problems, install WinPCAP/Npcap from:\n"); + pt_log(kLog_info, "https://www.winpcap.org/install/default.htm or Npcap for WIN10: https://nmap.org/npcap/windows-10.html\n"); pt_log(kLog_info, "After WinPCAP is installed, you can list pcap devices with: --list-pcap-devices\n"); } #endif @@ -252,8 +278,8 @@ int main(int argc, char *argv[]) { } #endif /* !WIN32 */ - pthread_mutex_init(&chain_lock, 0); - pthread_mutex_init(&num_threads_lock, 0); + pthread_mutex_init(&chain_lock, 0); + pthread_mutex_init(&num_threads_lock, 0); // Check mode, validate arguments and start either client or proxy. if (opts.mode == kMode_forward) { @@ -676,7 +702,7 @@ void* pt_proxy(void *args) { for (cur = chain; cur; cur = cur->next) { in_addr.s_addr = cur->dst_ip; if (cur->last_activity + kAutomatic_close_timeout < now) { - pt_log(kLog_info, "Dropping tunnel to %s:%d due to inactivity.\n", inet_ntoa(in_addr), cur->dst_port, cur->id_no); + pt_log(kLog_info, "Dropping tunnel %u to %s:%u due to inactivity.\n", cur->id_no, inet_ntoa(in_addr), cur->dst_port); cur->should_remove = 1; continue; } diff --git a/src/utils.c b/src/utils.c index a65f947..8e340d4 100644 --- a/src/utils.c +++ b/src/utils.c @@ -160,7 +160,7 @@ int pt_random(void) { #ifdef HAVE_ARC4RANDOM return arc4random(); #else -#if defined(USE_CUSTOMRNG) && !defined(_WIN32) +#if defined(RNGDEV) && !defined(_WIN32) static int rng_fd = -1; ssize_t bytes_read; int rnd_val; |