From 81c3f16a66f9afdb0ca24a254384fd041ff1093f Mon Sep 17 00:00:00 2001 From: David Sommerseth Date: Fri, 11 Mar 2022 00:31:28 +0100 Subject: [PATCH 001/346] version: Reset git:master reference to 3.8 Signed-off-by: David Sommerseth --- openvpn/common/version.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openvpn/common/version.hpp b/openvpn/common/version.hpp index 13b9b5de4..54e8c7ea8 100644 --- a/openvpn/common/version.hpp +++ b/openvpn/common/version.hpp @@ -24,5 +24,5 @@ #pragma once #ifndef OPENVPN_VERSION -#define OPENVPN_VERSION "3.7_git:master" +#define OPENVPN_VERSION "3.8_git:master" #endif From dc328c2ab242c640da4619466b4d9344f73b689d Mon Sep 17 00:00:00 2001 From: Frank Lichtenheld Date: Mon, 21 Mar 2022 14:56:05 +0100 Subject: [PATCH 002/346] ovpncli: Fix unitialized class member CID 10065 (#6 of 6): Uninitialized scalar field (UNINIT_CTOR) 2. uninit_member: Non-static class member proto_version_override is not initialized in this constructor nor in any functions that it calls. Signed-off-by: Frank Lichtenheld --- client/ovpncli.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ovpncli.cpp b/client/ovpncli.cpp index 81ef340ab..35717e580 100644 --- a/client/ovpncli.cpp +++ b/client/ovpncli.cpp @@ -433,7 +433,7 @@ namespace openvpn { std::string server_override; std::string port_override; Protocol proto_override; - IP::Addr::Version proto_version_override; + IP::Addr::Version proto_version_override = IP::Addr::Version::UNSPEC; TriStateSetting allowUnusedAddrFamilies; int conn_timeout = 0; bool tun_persist = false; From 2688ff49f1912cf48fba7a006ac66823229c434e Mon Sep 17 00:00:00 2001 From: Frank Lichtenheld Date: Mon, 21 Mar 2022 14:57:13 +0100 Subject: [PATCH 003/346] IPv6::Addr: add default for scope_id_ tunnetlink.hpp:368 CID 10637 (#5 of 5): Uninitialized scalar field (UNINIT_CTOR) 2. uninit_member: Non-static class member gw.scope_id_ is not initialized in this constructor nor in any functions that it calls. tunnetlink.hpp:176 CID 10653 (#5 of 5): Uninitialized scalar field (UNINIT_CTOR) 2. uninit_member: Non-static class member addr.scope_id_ is not initialized in this constructor nor in any functions that it calls. Signed-off-by: Frank Lichtenheld --- openvpn/addr/ipv6.hpp | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/openvpn/addr/ipv6.hpp b/openvpn/addr/ipv6.hpp index a10aa6d01..4fa65f5ff 100644 --- a/openvpn/addr/ipv6.hpp +++ b/openvpn/addr/ipv6.hpp @@ -75,7 +75,6 @@ namespace openvpn { { Addr ret; network_to_host_order(&ret.u, (const union ipv6addr *)in6->s6_addr); - ret.scope_id_ = 0; return ret; } @@ -149,7 +148,6 @@ namespace openvpn { static Addr from_hex(const std::string& s) { Addr ret; - ret.scope_id_ = 0; ret.zero(); size_t len = s.length(); size_t base = 0; @@ -198,7 +196,6 @@ namespace openvpn { static Addr from_ulong(unsigned long ul) { Addr ret; - ret.scope_id_ = 0; ret.u.u64[Endian::e2(0)] = std::uint64_t(ul); ret.u.u64[Endian::e2(1)] = 0; return ret; @@ -218,7 +215,6 @@ namespace openvpn { { bool neg = false; Addr ret; - ret.scope_id_ = 0; if (ul < 0) { ul = -(ul + 1); @@ -267,7 +263,6 @@ namespace openvpn { { Addr ret; network_to_host_order(&ret.u, (const union ipv6addr *)bytestr); - ret.scope_id_ = 0; return ret; } @@ -306,7 +301,6 @@ namespace openvpn { static Addr from_zero() { Addr ret; - ret.scope_id_ = 0; ret.zero(); return ret; } @@ -314,7 +308,6 @@ namespace openvpn { static Addr from_one() { Addr ret; - ret.scope_id_ = 0; ret.one(); return ret; } @@ -322,7 +315,6 @@ namespace openvpn { static Addr from_zero_complement() { Addr ret; - ret.scope_id_ = 0; ret.zero_complement(); return ret; } @@ -331,7 +323,6 @@ namespace openvpn { static Addr netmask_from_prefix_len(const unsigned int prefix_len) { Addr ret; - ret.scope_id_ = 0; ret.prefix_len_to_netmask(prefix_len); return ret; } @@ -554,7 +545,6 @@ namespace openvpn { if (hl < SIZE) { Addr a; - a.scope_id_ = 0; a.one(); return a << hl; } @@ -863,7 +853,7 @@ namespace openvpn { } union ipv6addr u; - unsigned int scope_id_; + unsigned int scope_id_ = 0; }; OPENVPN_OSTREAM(Addr, to_string) From a092a2dbb10ee7f866cfb5ef879e54988300f1ec Mon Sep 17 00:00:00 2001 From: James Yonan Date: Mon, 7 Mar 2022 19:57:00 -0700 Subject: [PATCH 004/346] WS::ClientSet: it seems more logical for SyncPersistState to be inside of HTTPStateContainer HTTPStateContainer contains the current HTTP client session state, and can be retained for multiple HTTP transactions over a persistent HTTP session. Since SyncPersistState can optionally contain the Asio io_context for synchronous operations, it seems logical to colocate this object with HTTPStateContainer. Signed-off-by: James Yonan --- openvpn/ws/httpcliset.hpp | 46 ++++++++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/openvpn/ws/httpcliset.hpp b/openvpn/ws/httpcliset.hpp index 7cccf8257..0bace9838 100644 --- a/openvpn/ws/httpcliset.hpp +++ b/openvpn/ws/httpcliset.hpp @@ -103,6 +103,22 @@ namespace openvpn { return alive() && c->http->host_match(host); } + // used for synchronous io_context + + std::unique_ptr acquire_io_context() + { + if (c) + return std::move(c->sps.io_context); + else + return std::unique_ptr(); + } + + void persist_io_context(std::unique_ptr&& io_context) + { + if (c) + c->sps.io_context = std::move(io_context); + } + #ifdef ASIO_HAS_LOCAL_SOCKETS int unix_fd() { @@ -121,6 +137,7 @@ namespace openvpn { struct Container : public RC { typedef RCPtr Ptr; + SyncPersistState sps; HTTPDelegate::Ptr http; }; @@ -155,6 +172,25 @@ namespace openvpn { Container::Ptr c; }; + // like HTTPStateContainer, but destructor automatically + // calls stop() method + class HTTPStateContainerAutoStop : public HTTPStateContainer + { + public: + HTTPStateContainerAutoStop(const bool shutdown) + : shutdown_(shutdown) + { + } + + ~HTTPStateContainerAutoStop() + { + HTTPStateContainer::stop(shutdown_); + } + + private: + const bool shutdown_; + }; + class TransactionSet; struct Transaction; @@ -304,12 +340,6 @@ namespace openvpn { class TransactionSet : public RC { - private: - // optionally contains an openvpn_io::io_context for - // persistent synchronous operations - friend class ClientSet; - SyncPersistState sps; - public: typedef RCPtr Ptr; typedef std::vector> Vector; @@ -498,7 +528,7 @@ namespace openvpn { }); ts->preserve_http_state = sps; if (sps) - io_context = std::move(ts->sps.io_context); + io_context = ts->hsc.acquire_io_context(); if (!io_context) io_context.reset(new openvpn_io::io_context(1)); ClientSet::Ptr cs; @@ -527,7 +557,7 @@ namespace openvpn { throw; } if (sps) - ts->sps.io_context = std::move(io_context); + ts->hsc.persist_io_context(std::move(io_context)); } static void run_synchronous(Function job, From ab55c9fdb20e65d861f6a35c679e5f878d55f163 Mon Sep 17 00:00:00 2001 From: Frank Lichtenheld Date: Thu, 31 Mar 2022 17:43:26 +0200 Subject: [PATCH 005/346] extpki.hpp: ignore deprecated EC_KEY_* functions Doing first -Werror builds on Linux against OpenSSL 3.0. Signed-off-by: Frank Lichtenheld --- openvpn/openssl/pki/extpki.hpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/openvpn/openssl/pki/extpki.hpp b/openvpn/openssl/pki/extpki.hpp index 8064173b9..2e2cbdca6 100644 --- a/openvpn/openssl/pki/extpki.hpp +++ b/openvpn/openssl/pki/extpki.hpp @@ -32,6 +32,12 @@ #include +// FIXME: don't use deprecated functions with OpenSSL 3 +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + namespace openvpn { using ssl_external_pki = SSLFactoryAPI::ssl_external_pki; @@ -218,7 +224,7 @@ namespace openvpn { unsigned int n_errors; }; - /* The OpenSSL EC_* methods we are using here are only available for OpennSSL 1.1.0 and later */ + /* The OpenSSL EC_* methods we are using here are only available for OpenSSL 1.1.0 and later */ #if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(OPENSSL_NO_EC) class ExternalPKIECImpl : public ExternalPKIImpl { @@ -417,3 +423,7 @@ namespace openvpn { #endif #endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(OPENSSL_NO_EC) */ } + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#pragma GCC diagnostic pop +#endif From 6715afd4c76573f9442c145c23a59dd06356e092 Mon Sep 17 00:00:00 2001 From: Frank Lichtenheld Date: Thu, 7 Apr 2022 12:10:45 +0200 Subject: [PATCH 006/346] test_ssl: fix ssl.enablelegacyProvider Since we didn't have any regular builds against OpenSSL 3.0 so far we didn't notice that it was broken by commit 291e675748316b910f9c526e21dedc53456b08f3 (Move SSL context from OpenSSL Context to OpenSSL Config) Since context is now part of config, we need to use separate configs. Signed-off-by: Frank Lichtenheld --- test/unittests/test_ssl.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/test/unittests/test_ssl.cpp b/test/unittests/test_ssl.cpp index cee531dbc..05e463470 100644 --- a/test/unittests/test_ssl.cpp +++ b/test/unittests/test_ssl.cpp @@ -126,13 +126,17 @@ TEST(ssl, enablelegacyProvider) auto f_nolegacy = sslcfg->new_factory(); - sslcfg->enable_legacy_algorithms(true); + EXPECT_EQ(SSLLib::CryptoAPI::CipherContext::is_supported(f_nolegacy->libctx(), openvpn::CryptoAlgs::BF_CBC), false); - /* Should not throw an error */ - auto f_legacy = sslcfg->new_factory(); + SSLLib::SSLAPI::Config::Ptr sslcfg_legacy(new SSLLib::SSLAPI::Config); + sslcfg_legacy->set_local_cert_enabled(false); + sslcfg_legacy->set_flags(SSLConst::NO_VERIFY_PEER); + sslcfg_legacy->set_rng(rng); + sslcfg_legacy->enable_legacy_algorithms(true); - EXPECT_EQ(SSLLib::CryptoAPI::CipherContext::is_supported(f_nolegacy->libctx(), openvpn::CryptoAlgs::BF_CBC), false); + /* Should not throw an error */ + auto f_legacy = sslcfg_legacy->new_factory(); EXPECT_EQ(SSLLib::CryptoAPI::CipherContext::is_supported(f_legacy->libctx(), openvpn::CryptoAlgs::BF_CBC), true); } -#endif \ No newline at end of file +#endif From 296abfca3210ed95df837a9cd4595aa8e7f934bf Mon Sep 17 00:00:00 2001 From: Lev Stipakov Date: Thu, 7 Apr 2022 13:42:36 +0300 Subject: [PATCH 007/346] mingw: fix broken OpenSSL checkout OpenSSL has changed tags naming to something like openssl-3.0.2, so adapt our script accordingly. Signed-off-by: Lev Stipakov --- scripts/mingw/build | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/mingw/build b/scripts/mingw/build index 8e05ee101..1d4569ddc 100755 --- a/scripts/mingw/build +++ b/scripts/mingw/build @@ -33,8 +33,8 @@ download_deps() if [ -z "$NO_OPENSSL" ]; then rm -rf openssl portfile_url=https://raw.githubusercontent.com/microsoft/vcpkg/master/ports/openssl/portfile.cmake - osslver=$(wget -q -O- "$portfile_url" | grep -oP '\bOPENSSL_VERSION\s+\S+' | cut -d' ' -f2 | tr -d ')' | tr . _) - git clone --single-branch --branch "OpenSSL_$osslver" https://github.com/openssl/openssl.git + osslver=$(wget -q -O- "$portfile_url" | grep -oP '\bOPENSSL_VERSION\s+\S+' | cut -d' ' -f2 | tr -d ')') + git clone --single-branch --branch "openssl-$osslver" https://github.com/openssl/openssl.git fi rm -rf tap-windows6 From f27157e28ba50ec3083afd4f86253e0f9d298589 Mon Sep 17 00:00:00 2001 From: Frank Lichtenheld Date: Thu, 7 Apr 2022 17:30:21 +0200 Subject: [PATCH 008/346] mingw: fix OpenSSL on x86_64 Hardcode libdir to lib, because openssl3 chooses lib64 otherwise. While here, some small changes: - remove dangerous "|| true" after openssl make - remove "-j1" for openssl "make install". Speeds up the documentation generation. - use set -x Signed-off-by: Frank Lichtenheld --- scripts/mingw/build | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/mingw/build b/scripts/mingw/build index 1d4569ddc..8a410c525 100755 --- a/scripts/mingw/build +++ b/scripts/mingw/build @@ -1,6 +1,6 @@ #!/usr/bin/env bash -set -e +set -ex DL="${DL:-/tmp/dl}" DEP_DIR_PREFIX=$PWD/deps @@ -114,8 +114,8 @@ build_openssl() [ "$ARCH" == "x86_64" ] && OUT="mingw64" || OUT="mingw" make clean || true - ./Configure --prefix=$DEP_DIR_PREFIX-$ARCH no-idea no-mdc2 no-rc5 shared $OUT --cross-compile-prefix=$ARCH-w64-mingw32- - make && make -j1 install || true + ./Configure --prefix=$DEP_DIR_PREFIX-$ARCH --libdir=lib no-idea no-mdc2 no-rc5 shared $OUT --cross-compile-prefix=$ARCH-w64-mingw32- + make && make install popd } From 171733b90f7acec15a87d619879e2530d1db0f97 Mon Sep 17 00:00:00 2001 From: James Yonan Date: Thu, 31 Mar 2022 12:20:00 -0600 Subject: [PATCH 009/346] Stop: added is_triggered() method Signed-off-by: James Yonan --- openvpn/common/stop.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openvpn/common/stop.hpp b/openvpn/common/stop.hpp index 467d2ed54..ef1a10a44 100644 --- a/openvpn/common/stop.hpp +++ b/openvpn/common/stop.hpp @@ -99,6 +99,11 @@ namespace openvpn { } } + static bool is_triggered(const Stop* stop) + { + return stop && stop->stop_called; + } + private: Stop(const Stop&) = delete; Stop& operator=(const Stop&) = delete; From 07936aed4cef3c248794a0f083f0d3f9508e7c60 Mon Sep 17 00:00:00 2001 From: James Yonan Date: Thu, 31 Mar 2022 12:21:19 -0600 Subject: [PATCH 010/346] Rand2: added defined() method Signed-off-by: James Yonan --- openvpn/random/rand2.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openvpn/random/rand2.hpp b/openvpn/random/rand2.hpp index 4f1acc800..9388e635c 100644 --- a/openvpn/random/rand2.hpp +++ b/openvpn/random/rand2.hpp @@ -48,6 +48,11 @@ namespace openvpn { { } + bool defined() const + { + return rng && prng; + } + RandomAPI::Ptr rng; RandomAPI::Ptr prng; }; From a68f70725c79a0b5bb0e1a2c5e6dea5b1f304ced Mon Sep 17 00:00:00 2001 From: James Yonan Date: Thu, 31 Mar 2022 12:22:23 -0600 Subject: [PATCH 011/346] AsioTimer/AsioTimerSafe: added convenience typedef UPtr Signed-off-by: James Yonan --- openvpn/time/asiotimer.hpp | 8 ++++---- openvpn/time/asiotimersafe.hpp | 2 ++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/openvpn/time/asiotimer.hpp b/openvpn/time/asiotimer.hpp index 543232000..187150915 100644 --- a/openvpn/time/asiotimer.hpp +++ b/openvpn/time/asiotimer.hpp @@ -22,10 +22,10 @@ // Create an Asio time_traits class to allow Asio to natively handle // our Time and Time::Duration classes. -#ifndef OPENVPN_TIME_ASIOTIMER_H -#define OPENVPN_TIME_ASIOTIMER_H +#pragma once #include +#include #include // was: #include @@ -64,6 +64,8 @@ namespace openvpn { class AsioTimer : public openvpn_io::basic_waitable_timer { public: + typedef std::unique_ptr UPtr; + AsioTimer(openvpn_io::io_context& io_context) : openvpn_io::basic_waitable_timer(io_context) { @@ -80,5 +82,3 @@ namespace openvpn { } }; } - -#endif diff --git a/openvpn/time/asiotimersafe.hpp b/openvpn/time/asiotimersafe.hpp index a5c227763..795f5db78 100644 --- a/openvpn/time/asiotimersafe.hpp +++ b/openvpn/time/asiotimersafe.hpp @@ -32,6 +32,8 @@ namespace openvpn { class AsioTimerSafe { public: + typedef std::unique_ptr UPtr; + AsioTimerSafe(openvpn_io::io_context& io_context) : timer_(io_context), epoch_(new Epoch) From 9367513b4ad37e97de789922716a9ddd3274849f Mon Sep 17 00:00:00 2001 From: Frank Lichtenheld Date: Tue, 12 Apr 2022 11:00:15 +0200 Subject: [PATCH 012/346] test_cpu_time: fix unused variable clang 13 complains: test/unittests/test_cpu_time.cpp:110:16: error: variable 'd' set but not used [-Werror,-Wunused-but-set-variable] double d=0; Signed-off-by: Frank Lichtenheld --- test/unittests/test_cpu_time.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/unittests/test_cpu_time.cpp b/test/unittests/test_cpu_time.cpp index 32819bf5e..bc80f8ae5 100644 --- a/test/unittests/test_cpu_time.cpp +++ b/test/unittests/test_cpu_time.cpp @@ -112,6 +112,7 @@ namespace unittests { d += gen(); } + (void)d; } From 89774d78be3828d05c975c18095a59bb1b8c6ca8 Mon Sep 17 00:00:00 2001 From: Frank Lichtenheld Date: Tue, 19 Apr 2022 11:40:53 +0200 Subject: [PATCH 013/346] memneq: use boolean operator for boolean operands clang 14 complains: openvpn/common/memneq.hpp:128:38: error: use of bitwise '|' with boolean operands [-Werror,-Wbitwise-instead-of-logical] return memneq_unaligned_ok || (is_aligned(a)|is_aligned(b)); Signed-off-by: Frank Lichtenheld --- openvpn/common/memneq.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openvpn/common/memneq.hpp b/openvpn/common/memneq.hpp index 6514a06ea..5bcc04d2a 100644 --- a/openvpn/common/memneq.hpp +++ b/openvpn/common/memneq.hpp @@ -125,7 +125,7 @@ namespace openvpn { // point to objects of type memneq_t. inline bool memneq_deref_ok(const void *a, const void *b) { - return memneq_unaligned_ok || (is_aligned(a)|is_aligned(b)); + return memneq_unaligned_ok || (is_aligned(a)||is_aligned(b)); } // Constant-time memory equality method. Can be used in From 11b0869b7f4cf4910d33ecc1c9984f13a22b6537 Mon Sep 17 00:00:00 2001 From: Lev Stipakov Date: Thu, 21 Apr 2022 17:37:27 +0300 Subject: [PATCH 014/346] dco-win: improve stability Add missing halt etc checks. Replace dco_error with fatal exception ErrorCode(TUN_SETUP_FAILED). IOCTL commands are not expected to fail and of they do, threat it as fatal error. Signed-off-by: Lev Stipakov --- openvpn/dco/dcocli.hpp | 2 -- openvpn/dco/ovpndcowincli.hpp | 17 ++++++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/openvpn/dco/dcocli.hpp b/openvpn/dco/dcocli.hpp index ee3a6fdd7..5ac31be31 100644 --- a/openvpn/dco/dcocli.hpp +++ b/openvpn/dco/dcocli.hpp @@ -67,8 +67,6 @@ enum { OVPN_PEER_ID_UNDEF = 0x00FFFFFF, }; -OPENVPN_EXCEPTION(dco_error); - class ClientConfig : public DCO, public TransportClientFactory, public TunClientFactory { diff --git a/openvpn/dco/ovpndcowincli.hpp b/openvpn/dco/ovpndcowincli.hpp index d945e343b..83a4e818e 100644 --- a/openvpn/dco/ovpndcowincli.hpp +++ b/openvpn/dco/ovpndcowincli.hpp @@ -184,10 +184,13 @@ class OvpnDcoWinClient : public Client, public KoRekey::Receiver { buf_.mutable_buffer_clamp(), [self = Ptr(this)](const openvpn_io::error_code &error, const size_t bytes_recvd) { + if (self->halt) + return; if (!error) { self->buf_.set_size(bytes_recvd); self->transport_parent->transport_recv(self->buf_); - self->queue_read_(); + if (!self->halt) + self->queue_read_(); } else if (!self->halt) { self->stop_(); self->transport_parent->transport_error(Error::TRANSPORT_ERROR, @@ -214,7 +217,8 @@ class OvpnDcoWinClient : public Client, public KoRekey::Receiver { std::ostringstream os; halt = true; async_resolve_cancel(); - tun_setup_->destroy(os); + if (tun_setup_) + tun_setup_->destroy(os); handle_.reset(); OPENVPN_LOG_STRING(os.str()); } @@ -298,8 +302,7 @@ class OvpnDcoWinClient : public Client, public KoRekey::Receiver { void add_crypto_(const CryptoDCInstance::RekeyType type, const KoRekey::KeyConfig *kc) { if (kc->cipher_alg != OVPN_CIPHER_ALG_AES_GCM) { - OPENVPN_LOG("unsupported cipher for DCO"); - throw dco_error(); + throw ErrorCode(Error::TUN_SETUP_FAILED, true, "unsupported cipher for DCO"); } OVPN_CRYPTO_DATA data; @@ -360,9 +363,9 @@ class OvpnDcoWinClient : public Client, public KoRekey::Receiver { ov->complete(error, 0); } - OPENVPN_LOG("DeviceIoControl(" << code_str.at(code) << ")" - << " failed with code " << error_code); - throw dco_error(); + std::ostringstream os; + os << "DeviceIoControl(" << code_str.at(code) << ")" << " failed with code " << error_code; + throw ErrorCode(Error::TUN_SETUP_FAILED, true, os.str()); } return ERROR_SUCCESS; } From 481a4319e2b3e1f0e932d08652f1299db13a86c9 Mon Sep 17 00:00:00 2001 From: Lev Stipakov Date: Thu, 21 Apr 2022 18:21:30 +0300 Subject: [PATCH 015/346] dco-win: support for 0.7.1 driver version Use the latest uapi header. Instead of using vcpkg port overlay, copy file into repository. Signed-off-by: Lev Stipakov --- openvpn/dco/dcocli.hpp | 2 +- openvpn/dco/ovpn-dco.h | 129 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 openvpn/dco/ovpn-dco.h diff --git a/openvpn/dco/dcocli.hpp b/openvpn/dco/dcocli.hpp index 5ac31be31..60d626df4 100644 --- a/openvpn/dco/dcocli.hpp +++ b/openvpn/dco/dcocli.hpp @@ -52,7 +52,7 @@ #include #elif ENABLE_OVPNDCOWIN #include -#include +#include #else #error either ENABLE_KOVPN, ENABLE_OVPNDCO or ENABLE_OVPNDCOWIN must be defined #endif diff --git a/openvpn/dco/ovpn-dco.h b/openvpn/dco/ovpn-dco.h new file mode 100644 index 000000000..12e8d3395 --- /dev/null +++ b/openvpn/dco/ovpn-dco.h @@ -0,0 +1,129 @@ +/* + * ovpn-dco-win OpenVPN protocol accelerator for Windows + * + * Copyright (C) 2020-2021 OpenVPN Inc + * + * Author: Lev Stipakov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + + /* + This particular file (ovpn-dco.h) is also licensed using the MIT License. + + Copyright 2022 OpenVPN, Inc + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files(the "Software"), to deal in the + Software without restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and /or sell copies of the + Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions : + + The above copyright noticeand this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR + COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once +#ifndef _KERNEL_MODE +#include +#endif +#include +#include + +typedef enum { + OVPN_PROTO_UDP, + OVPN_PROTO_TCP +} OVPN_PROTO; + +typedef struct _OVPN_NEW_PEER { + union { + SOCKADDR_IN Addr4; + SOCKADDR_IN6 Addr6; + } Local; + + union { + SOCKADDR_IN Addr4; + SOCKADDR_IN6 Addr6; + } Remote; + + OVPN_PROTO Proto; +} OVPN_NEW_PEER, * POVPN_NEW_PEER; + +typedef struct _OVPN_STATS { + LONG LostInControlPackets; + LONG LostOutControlPackets; + + LONG LostInDataPackets; + LONG LostOutDataPackets; + + LONG ReceivedDataPackets; + LONG ReceivedControlPackets; + + LONG SentControlPackets; + LONG SentDataPackets; + + LONG64 TransportBytesSent; + LONG64 TransportBytesReceived; + + LONG64 TunBytesSent; + LONG64 TunBytesReceived; +} OVPN_STATS, * POVPN_STATS; + +typedef enum _OVPN_KEY_SLOT { + OVPN_KEY_SLOT_PRIMARY, + OVPN_KEY_SLOT_SECONDARY +} OVPN_KEY_SLOT; + +typedef enum _OVPN_CIPHER_ALG { + OVPN_CIPHER_ALG_NONE, + OVPN_CIPHER_ALG_AES_GCM, + OVPN_CIPHER_ALG_CHACHA20_POLY1305 +} OVPN_CIPHER_ALG; + +typedef struct _OVPN_KEY_DIRECTION +{ + unsigned char Key[32]; + unsigned char KeyLen; // 16/24/32 -> AES-128-GCM/AES-192-GCM/AES-256-GCM + unsigned char NonceTail[8]; +} OVPN_KEY_DIRECTION; + +typedef struct _OVPN_CRYPTO_DATA { + OVPN_KEY_DIRECTION Encrypt; + OVPN_KEY_DIRECTION Decrypt; + OVPN_KEY_SLOT KeySlot; + OVPN_CIPHER_ALG CipherAlg; + unsigned char KeyId; + int PeerId; +} OVPN_CRYPTO_DATA, * POVPN_CRYPTO_DATA; + +typedef struct _OVPN_SET_PEER { + LONG KeepaliveInterval; + LONG KeepaliveTimeout; + LONG MSS; +} OVPN_SET_PEER, * POVPN_SET_PEER; + +#define OVPN_IOCTL_NEW_PEER CTL_CODE(FILE_DEVICE_UNKNOWN, 1, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define OVPN_IOCTL_GET_STATS CTL_CODE(FILE_DEVICE_UNKNOWN, 2, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define OVPN_IOCTL_NEW_KEY CTL_CODE(FILE_DEVICE_UNKNOWN, 3, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define OVPN_IOCTL_SWAP_KEYS CTL_CODE(FILE_DEVICE_UNKNOWN, 4, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define OVPN_IOCTL_SET_PEER CTL_CODE(FILE_DEVICE_UNKNOWN, 5, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define OVPN_IOCTL_START_VPN CTL_CODE(FILE_DEVICE_UNKNOWN, 6, METHOD_BUFFERED, FILE_ANY_ACCESS) From 56dcd976b1bb0e8000d3140cbd35dadcd1ad6caa Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Fri, 29 Apr 2022 17:15:26 +0200 Subject: [PATCH 016/346] Document webauth fallback when REST API for profile download is used This add a way to signal that webauth needs to be used when a client erroursnly uses REST to try to download a profile. Signed-off-by: Arne Schwabe --- doc/webauth.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/doc/webauth.md b/doc/webauth.md index 42a847b9b..0e8c4ce5f 100644 --- a/doc/webauth.md +++ b/doc/webauth.md @@ -177,6 +177,7 @@ The flags are also comma separated values. Currently, the followings flag that a * hidden-webview Starts the webview in hidden mode. See the web auth section for more details * external Indicates that an internal webivew should NOT be used but instead a normal browser is to be used. + * internal Indicates that the internal webview should be used if possible In general websites should also report ovpn-webauth without `embedded=true` parameter to allow clients without internal browser support to craft a url to open in an external browser that @@ -329,6 +330,24 @@ User is not enrolled through the WEB client yet: You must enroll this user in Authenticator first before you are allowed to retrieve a connection profile. (9008) +Webauth fallback +---------------- +This is used when the server is configured to use username/password as general +authentication method but some users are setup to used the web based +authentication method. Should a user that requires web based try to authenticate +instead it will report an error: + + + Authorization Required + REST method failed + Ovpn-WebAuth: providername,flags + + +The format and meaning of the Ovpn-WebAuth is identical to the one used in the +detection of web based profile download. If the client encounters this error it +should offer the user to continue to the import using the web based profile +download method. + Challenge/response authentication --------------------------------- The challenge/response protocol for the Rest web api mirrors the approach From b7c6e2d5af4357bcf25f7a9bfc0a3ecc8cf66ebe Mon Sep 17 00:00:00 2001 From: Lev Stipakov Date: Thu, 28 Apr 2022 16:03:29 +0300 Subject: [PATCH 017/346] dco-win: remove ovpn-dco-win vcpkg dependency uapi header has been added to the source code, so we don't need that dependency anymore. Fix MinGW script accordingly. Signed-off-by: Lev Stipakov --- scripts/mingw/build | 13 +------------ test/ovpncli/CMakeLists.txt | 5 ----- vcpkg.json | 3 +-- 3 files changed, 2 insertions(+), 19 deletions(-) diff --git a/scripts/mingw/build b/scripts/mingw/build index 8a410c525..11a110717 100755 --- a/scripts/mingw/build +++ b/scripts/mingw/build @@ -127,15 +127,6 @@ build_tap_windows6() cp $DL/tap-windows6/src/tap-windows.h $DEP_DIR_PREFIX-$ARCH/include } -build_ovpn_dco_win() -{ - ARCH=$1 - local DST_DIR="$DEP_DIR_PREFIX-$ARCH/include/ovpn-dco-win" - - mkdir -p $DST_DIR - cp $DL/ovpn-dco-win/uapi.h $DST_DIR/ -} - build_asio() { ARCH=$1 @@ -160,7 +151,6 @@ build_deps() build_lz4 $ARCH build_jsoncpp $ARCH build_tap_windows6 $ARCH - build_ovpn_dco_win $ARCH build_asio $ARCH build_openssl $ARCH } @@ -175,8 +165,7 @@ build_core() mkdir build-$ARCH [ -z "$DCO" ] || { - WITH_OVPNDCOWIN="-D CLI_OVPNDCOWIN=ON \ - -D OVPN_DCO_WIN_INCLUDE_DIRS=$DEP_DIR_PREFIX-$ARCH" + WITH_OVPNDCOWIN="-D CLI_OVPNDCOWIN=ON" } pushd build-$ARCH diff --git a/test/ovpncli/CMakeLists.txt b/test/ovpncli/CMakeLists.txt index 3cf59c780..200fb30a7 100644 --- a/test/ovpncli/CMakeLists.txt +++ b/test/ovpncli/CMakeLists.txt @@ -46,13 +46,8 @@ if (WIN32) OVPNAGENT_DISABLE_PATH_CHECK) if (${CLI_OVPNDCOWIN}) - find_path(OVPN_DCO_WIN_INCLUDE_DIRS "ovpn-dco-win/uapi.h") - target_compile_definitions(ovpncliagent PRIVATE ENABLE_OVPNDCOWIN) - target_include_directories(ovpncliagent PRIVATE ${OVPN_DCO_WIN_INCLUDE_DIRS}) - target_compile_definitions(ovpncli PRIVATE ENABLE_OVPNDCOWIN) - target_include_directories(ovpncli PRIVATE ${OVPN_DCO_WIN_INCLUDE_DIRS}) endif() endif () diff --git a/vcpkg.json b/vcpkg.json index 6e69acdd3..22a326eb2 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -8,7 +8,6 @@ "openssl", "jsoncpp", "asio", - "tap-windows6", - "ovpn-dco-win" + "tap-windows6" ] } From ac15879588b43a10d18bef983946ae333670fe45 Mon Sep 17 00:00:00 2001 From: Lev Stipakov Date: Thu, 28 Apr 2022 16:18:03 +0300 Subject: [PATCH 018/346] support for default mssfix Add support for default mssfix, which is calculated based on upper bound value 1492 minus payload and encapculation overhead. Payload overhead includes: - compression byte (except for V2, which doesn't add overhead) - pktid (in CBC) - IPv4 and TCP headers Encapculation overhead: - crypto overhead (for AEAD 16 bytes auth tag, 4 bytes pktid, 4 bytes opcode/peer-id = 24) - 2 bytes packet size for TCP transport Also for CBC we must take padding [1..blocksize] into account. Signed-off-by: Lev Stipakov --- openvpn/client/cliproto.hpp | 7 +- openvpn/dco/dcocli.hpp | 3 +- openvpn/ssl/mssparms.hpp | 20 +++-- openvpn/ssl/proto.hpp | 115 +++++++++++++++++------- openvpn/transport/mssfix.hpp | 8 +- openvpn/tun/linux/client/tuncli.hpp | 2 +- openvpn/tun/linux/client/tunnetlink.hpp | 2 +- openvpn/tun/mac/client/tuncli.hpp | 3 +- openvpn/tun/tunmtu.hpp | 4 + 9 files changed, 114 insertions(+), 50 deletions(-) diff --git a/openvpn/client/cliproto.hpp b/openvpn/client/cliproto.hpp index 3d2287153..d2c005e68 100644 --- a/openvpn/client/cliproto.hpp +++ b/openvpn/client/cliproto.hpp @@ -381,9 +381,12 @@ namespace openvpn { if (buf.size()) { const ProtoContext::Config& c = Base::conf(); - if (c.mss_inter > 0 && buf.size() > c.mss_inter) + // when calculating mss, we take IPv4 and TCP headers into account + // here we need to add it back since we check the whole IP packet size, not just TCP payload + size_t mss_no_tcp_ip_encap = (size_t)c.mss_fix + (20 + 20); + if (c.mss_fix > 0 && buf.size() > mss_no_tcp_ip_encap) { - Ptb::generate_icmp_ptb(buf, c.mss_inter); + Ptb::generate_icmp_ptb(buf, mss_no_tcp_ip_encap); tun->tun_send(buf); } else diff --git a/openvpn/dco/dcocli.hpp b/openvpn/dco/dcocli.hpp index 60d626df4..ebc1efa7b 100644 --- a/openvpn/dco/dcocli.hpp +++ b/openvpn/dco/dcocli.hpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -93,7 +94,7 @@ class ClientConfig : public DCO, // set a default MTU if (!tun.tun_prop.mtu) - tun.tun_prop.mtu = 1500; + tun.tun_prop.mtu = TUN_MTU_DEFAULT; // parse "dev" option { diff --git a/openvpn/ssl/mssparms.hpp b/openvpn/ssl/mssparms.hpp index 7289fb026..3d73e6920 100644 --- a/openvpn/ssl/mssparms.hpp +++ b/openvpn/ssl/mssparms.hpp @@ -28,10 +28,9 @@ namespace openvpn { struct MSSParms { - MSSParms() : mssfix(0), - mtu(false) - { - } + enum { + MSSFIX_DEFAULT = 1492, + }; void parse(const OptionList& opt, bool nothrow=false) { @@ -44,6 +43,7 @@ namespace openvpn { if (nothrow) { OPENVPN_LOG("Missing mssfix value, mssfix functionality disabled"); + mssfix_default = false; return; } else @@ -63,17 +63,25 @@ namespace openvpn { if (*val != "0") { OPENVPN_LOG("Invalid mssfix value " << *val << ", mssfix functionality disabled"); + mssfix_default = false; } } else throw option_error("mssfix: parse/range issue"); } + else + { + mssfix_default = false; + } mtu = (o->get_optional(2, 16) == "mtu"); + fixed = (o->get_optional(2, 16) == "fixed"); } } - unsigned int mssfix; // standard OpenVPN mssfix parm - bool mtu; // consider transport packet overhead in MSS adjustment + unsigned int mssfix = 0; // standard OpenVPN mssfix parm + bool mtu = false; // include overhead from IP and TCP/UDP encapsulation + bool fixed = false; // use mssfix value without any encapsulation adjustments + bool mssfix_default = true; }; struct MSSCtrlParms diff --git a/openvpn/ssl/proto.hpp b/openvpn/ssl/proto.hpp index 0c0f56af5..faf46e3c8 100644 --- a/openvpn/ssl/proto.hpp +++ b/openvpn/ssl/proto.hpp @@ -351,9 +351,9 @@ namespace openvpn { int local_peer_id = -1; // -1 to disable // MTU - unsigned int tun_mtu = 1500; + unsigned int tun_mtu = TUN_MTU_DEFAULT; MSSParms mss_parms; - unsigned int mss_inter = 0; + unsigned int mss_fix = 0; // Debugging int debug_level = 1; @@ -571,6 +571,19 @@ namespace openvpn { // mssfix mss_parms.parse(opt, true); + if (mss_parms.mssfix_default) + { + if (tun_mtu == TUN_MTU_DEFAULT) + { + mss_parms.mssfix = MSSParms::MSSFIX_DEFAULT; + mss_parms.mtu = true; + } + else + { + mss_parms.mssfix = tun_mtu; + mss_parms.fixed = true; + } + } // load parameters that can be present in both config file or pushed options load_common(opt, pco, server ? LOAD_COMMON_SERVER : LOAD_COMMON_CLIENT); @@ -1636,8 +1649,8 @@ namespace openvpn { compress->decompress(buf); // set MSS for segments server can receive - if (proto.config->mss_inter > 0) - MSSFix::mssfix(buf, proto.config->mss_inter); + if (proto.config->mss_fix > 0) + MSSFix::mssfix(buf, proto.config->mss_fix); } else buf.reset_size(); // no crypto context available @@ -1830,6 +1843,67 @@ namespace openvpn { dck.swap(data_channel_key); } + void calculate_mssfix(Config& c) + { + if (c.mss_parms.fixed) + { + // substract IPv4 and TCP overhead, mssfix method will add extra 20 bytes for IPv6 + c.mss_fix = c.mss_parms.mssfix - (20 + 20); + OPENVPN_LOG("fixed mssfix=" << c.mss_fix); + return; + } + + int payload_overhead = 0; + + // compv2 doesn't increase payload size + switch (c.comp_ctx.type()) + { + case CompressContext::NONE: + case CompressContext::COMP_STUBv2: + case CompressContext::LZ4v2: + break; + default: + payload_overhead += 1; + } + + if (CryptoAlgs::mode(c.dc.cipher()) == CryptoAlgs::CBC_HMAC) + payload_overhead += PacketID::size(PacketID::SHORT_FORM); + + // account for IPv4 and TCP headers of the payload, mssfix method will add 20 extra bytes if payload is IPv6 + payload_overhead += 20 + 20; + + int overhead = c.protocol.extra_transport_bytes() + + (enable_op32 ? OP_SIZE_V2 : 1) + + c.dc.context().encap_overhead(); + + // in CBC mode, the packet id is part of the payload size / overhead + if (CryptoAlgs::mode(c.dc.cipher()) != CryptoAlgs::CBC_HMAC) + overhead += PacketID::size(PacketID::SHORT_FORM); + + if (c.mss_parms.mtu) + { + overhead += c.protocol.is_ipv6() ? sizeof(struct IPv6Header) : sizeof(struct IPv4Header); + overhead += proto.is_tcp() ? sizeof(struct TCPHeader) : sizeof(struct UDPHeader); + } + + int target = c.mss_parms.mssfix - overhead; + if (CryptoAlgs::mode(c.dc.cipher()) == CryptoAlgs::CBC_HMAC) + { + // openvpn3 crypto includes blocksize in overhead, but we can be a bit smarter here + // and instead make sure that resulting ciphertext size (which is always multiple blocksize) + // is not larger than target by running down target to the nearest multiple of multiple and substracting 1. + + int block_size = CryptoAlgs::block_size(c.dc.cipher()); + target += block_size; + target = (target / block_size) * block_size; + target -= 1; + } + + c.mss_fix = target - payload_overhead; + OPENVPN_LOG("mssfix=" << c.mss_fix << " (upper bound=" << c.mss_parms.mssfix << ", overhead=" << + overhead << ", payload_overhead=" << payload_overhead << ", target=" << target << ")"); + } + // Initialize the components of the OpenVPN data channel protocol void init_data_channel() { @@ -1890,34 +1964,7 @@ namespace openvpn { // cache op32 for hot path in do_encrypt cache_op32(); - int crypto_encap = (enable_op32 ? OP_SIZE_V2 : 1) + - c.comp_ctx.extra_payload_bytes() + - PacketID::size(PacketID::SHORT_FORM) + - c.dc.context().encap_overhead(); - - int transport_encap = 0; - if (c.mss_parms.mtu) - { - if (proto.is_tcp()) - transport_encap += sizeof(struct TCPHeader); - else - transport_encap += sizeof(struct UDPHeader); - - if (c.protocol.is_ipv6()) - transport_encap += sizeof(struct IPv6Header); - else - transport_encap += sizeof(struct IPv4Header); - - transport_encap += c.protocol.extra_transport_bytes(); - } - - if (c.mss_parms.mssfix != 0) - { - OPENVPN_LOG_PROTO("MTU mssfix=" << c.mss_parms.mssfix << - " crypto_encap=" << crypto_encap << - " transport_encap=" << transport_encap); - c.mss_inter = c.mss_parms.mssfix - (crypto_encap + transport_encap); - } + calculate_mssfix(c); } void data_limit_notify(const DataLimit::Mode cdl_mode, @@ -2069,8 +2116,8 @@ namespace openvpn { bool pid_wrap; // set MSS for segments client can receive - if (proto.config->mss_inter > 0) - MSSFix::mssfix(buf, proto.config->mss_inter); + if (proto.config->mss_fix > 0) + MSSFix::mssfix(buf, proto.config->mss_fix); // compress packet if (compress) diff --git a/openvpn/transport/mssfix.hpp b/openvpn/transport/mssfix.hpp index e275f6e90..ac833c5e1 100644 --- a/openvpn/transport/mssfix.hpp +++ b/openvpn/transport/mssfix.hpp @@ -36,7 +36,7 @@ namespace openvpn { class MSSFix { public: - static void mssfix(BufferAllocated& buf, int mss_inter) + static void mssfix(BufferAllocated& buf, int mss_fix) { if (buf.empty()) return; @@ -61,7 +61,7 @@ namespace openvpn { TCPHeader* tcphdr = (TCPHeader*)(buf.data() + ipv4hlen); int ip_payload_len = buf.length() - ipv4hlen; - do_mssfix(tcphdr, mss_inter - (sizeof(struct IPv4Header) + sizeof(struct TCPHeader)), ip_payload_len); + do_mssfix(tcphdr, mss_fix, ip_payload_len); } } break; @@ -96,8 +96,8 @@ namespace openvpn { if (payload_len >= (int) sizeof(struct TCPHeader)) { TCPHeader *tcphdr = (TCPHeader *)(buf.data() + sizeof(struct IPv6Header)); - do_mssfix(tcphdr, mss_inter - (sizeof(struct IPv6Header) + sizeof(struct TCPHeader)), - payload_len); + // mssfix is calculated for IPv4, and since IPv6 header is 20 bytes larger we need to account for it + do_mssfix(tcphdr, mss_fix - 20, payload_len); } } break; diff --git a/openvpn/tun/linux/client/tuncli.hpp b/openvpn/tun/linux/client/tuncli.hpp index accfd93e6..87114240f 100644 --- a/openvpn/tun/linux/client/tuncli.hpp +++ b/openvpn/tun/linux/client/tuncli.hpp @@ -89,7 +89,7 @@ namespace openvpn { { // set a default MTU if (!tun_prop.mtu) - tun_prop.mtu = 1500; + tun_prop.mtu = TUN_MTU_DEFAULT; // parse "dev" option if (dev_name.empty()) diff --git a/openvpn/tun/linux/client/tunnetlink.hpp b/openvpn/tun/linux/client/tunnetlink.hpp index f0c450609..edb58962c 100644 --- a/openvpn/tun/linux/client/tunnetlink.hpp +++ b/openvpn/tun/linux/client/tunnetlink.hpp @@ -97,7 +97,7 @@ namespace openvpn { std::string dev; bool up = true; - int mtu = 1500; + int mtu = TUN_MTU_DEFAULT; }; struct NetlinkAddr4 : public Action diff --git a/openvpn/tun/mac/client/tuncli.hpp b/openvpn/tun/mac/client/tuncli.hpp index c6dbfae91..10267b43e 100644 --- a/openvpn/tun/mac/client/tuncli.hpp +++ b/openvpn/tun/mac/client/tuncli.hpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #ifdef TEST_EER // test emulated exclude routes @@ -199,7 +200,7 @@ namespace openvpn { // handle MTU default if (!po->mtu) - po->mtu = 1500; + po->mtu = TUN_MTU_DEFAULT; OPENVPN_LOG("CAPTURED OPTIONS:" << std::endl << po->to_string()); diff --git a/openvpn/tun/tunmtu.hpp b/openvpn/tun/tunmtu.hpp index 7b1181c79..6fc96b6c3 100644 --- a/openvpn/tun/tunmtu.hpp +++ b/openvpn/tun/tunmtu.hpp @@ -25,6 +25,10 @@ #include namespace openvpn { + enum { + TUN_MTU_DEFAULT = 1500, + }; + inline unsigned int parse_tun_mtu(const OptionList& opt, unsigned int default_value) { return opt.get_num("tun-mtu", 1, default_value, 576, 65535); From 2b7f97c78b67f380c2872ce6c6f510ed34a02210 Mon Sep 17 00:00:00 2001 From: Lev Stipakov Date: Fri, 29 Apr 2022 14:44:28 +0300 Subject: [PATCH 019/346] dco-win: reorder ioctl calls Set keepalive parameters and start vpn on "tun start" event, which seems like a more logical place comparison to "primary key ready" event. Signed-off-by: Lev Stipakov --- openvpn/dco/ovpndcowincli.hpp | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/openvpn/dco/ovpndcowincli.hpp b/openvpn/dco/ovpndcowincli.hpp index 83a4e818e..a158ebfdd 100644 --- a/openvpn/dco/ovpndcowincli.hpp +++ b/openvpn/dco/ovpndcowincli.hpp @@ -77,6 +77,9 @@ class OvpnDcoWinClient : public Client, public KoRekey::Receiver { dc_settings.set_factory(CryptoDCFactory::Ptr(new KoRekey::Factory( dc_settings.factory(), this, config->transport.frame))); + set_keepalive_(); + start_vpn_(); + tun_parent->tun_connected(); } @@ -97,9 +100,7 @@ class OvpnDcoWinClient : public Client, public KoRekey::Receiver { switch (rktype) { case CryptoDCInstance::ACTIVATE_PRIMARY: - add_keepalive_(); add_crypto_(rktype, key()); - start_vpn_(); break; case CryptoDCInstance::NEW_SECONDARY: @@ -276,7 +277,7 @@ class OvpnDcoWinClient : public Client, public KoRekey::Receiver { } } - void add_keepalive_() { + void set_keepalive_() { if (!transport_parent->is_keepalive_enabled()) return; @@ -285,17 +286,13 @@ class OvpnDcoWinClient : public Client, public KoRekey::Receiver { // since userspace doesn't know anything about presense or // absense of data channel traffic, ping should be handled in kernel - transport_parent->disable_keepalive(keepalive_interval, - keepalive_timeout); + transport_parent->disable_keepalive(keepalive_interval, keepalive_timeout); if (config->ping_restart_override) keepalive_timeout = config->ping_restart_override; // enable keepalive in kernel - OVPN_SET_PEER peer = {}; - peer.KeepaliveInterval = static_cast(keepalive_interval); - peer.KeepaliveTimeout = static_cast(keepalive_timeout); - + OVPN_SET_PEER peer{static_cast(keepalive_interval), static_cast(keepalive_timeout), -1}; dco_ioctl_(OVPN_IOCTL_SET_PEER, &peer, sizeof(peer)); } From 3d662431fa360472cd5e03802ce0d665b47a62fb Mon Sep 17 00:00:00 2001 From: Lev Stipakov Date: Fri, 29 Apr 2022 14:56:02 +0300 Subject: [PATCH 020/346] dco-win: pass mss to kernel Pass mss_fix value to dco-win driver via set_peer call. Signed-off-by: Lev Stipakov --- openvpn/client/cliproto.hpp | 3 +++ openvpn/dco/ovpndcowincli.hpp | 6 ++++++ openvpn/tun/client/tunbase.hpp | 2 ++ 3 files changed, 11 insertions(+) diff --git a/openvpn/client/cliproto.hpp b/openvpn/client/cliproto.hpp index d2c005e68..4b24bcbc0 100644 --- a/openvpn/client/cliproto.hpp +++ b/openvpn/client/cliproto.hpp @@ -608,6 +608,9 @@ namespace openvpn { // initialize data channel after pushed options have been processed Base::init_data_channel(); + // we got pushed options and initializated crypto - now we can push mss to dco + tun->adjust_mss(conf().mss_fix); + // Allow ProtoContext to suggest an alignment adjustment // hint for transport layer. transport->reset_align_adjust(Base::align_adjust_hint()); diff --git a/openvpn/dco/ovpndcowincli.hpp b/openvpn/dco/ovpndcowincli.hpp index a158ebfdd..5654282d0 100644 --- a/openvpn/dco/ovpndcowincli.hpp +++ b/openvpn/dco/ovpndcowincli.hpp @@ -85,6 +85,12 @@ class OvpnDcoWinClient : public Client, public KoRekey::Receiver { std::string tun_name() const override { return "ovpn-dco-win"; } + void adjust_mss(int mss) override + { + OVPN_SET_PEER peer {-1, -1, mss}; + dco_ioctl_(OVPN_IOCTL_SET_PEER, &peer, sizeof(peer)); + } + IP::Addr server_endpoint_addr() const override { return IP::Addr::from_asio(endpoint_.address()); } diff --git a/openvpn/tun/client/tunbase.hpp b/openvpn/tun/client/tunbase.hpp index e9b96f4d0..acc87911c 100644 --- a/openvpn/tun/client/tunbase.hpp +++ b/openvpn/tun/client/tunbase.hpp @@ -52,6 +52,8 @@ namespace openvpn { virtual std::string vpn_gw4() const { return std::string(); } // VPN gateways virtual std::string vpn_gw6() const { return std::string(); } + + virtual void adjust_mss(int mss) {}; }; // Base class for parent of tun interface object, used to From 77c5ba9a062d53f54d735531df94312e285bea44 Mon Sep 17 00:00:00 2001 From: Lev Stipakov Date: Mon, 2 May 2022 12:33:40 +0300 Subject: [PATCH 021/346] dco: initialize DCO controller after allowing default ciphers Allowing default ciphers overwrites state which is set in DCO controller initialization, so DCO controller initialization has to be moved after allowing default ciphers. Signed-off-by: Lev Stipakov --- openvpn/client/cliopt.hpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/openvpn/client/cliopt.hpp b/openvpn/client/cliopt.hpp index 401799519..50bd7b6e5 100644 --- a/openvpn/client/cliopt.hpp +++ b/openvpn/client/cliopt.hpp @@ -211,15 +211,6 @@ namespace openvpn { ,extern_transport_factory(config.extern_transport_factory) #endif { -#if (defined(ENABLE_KOVPN) || defined(ENABLE_OVPNDCO) || defined(ENABLE_OVPNDCOWIN)) && !defined(OPENVPN_FORCE_TUN_NULL) && !defined(OPENVPN_EXTERNAL_TUN_FACTORY) - if (config.dco) -#if defined(USE_TUN_BUILDER) - dco = DCOTransport::new_controller(config.builder); -#else - dco = DCOTransport::new_controller(nullptr); -#endif -#endif - // parse general client options const ParseClientConfig pcc(opt); @@ -254,7 +245,16 @@ namespace openvpn { !config.enable_nonpreferred_dcalgs, config.enable_legacy_algorithms); - layer = cp_main->layer; +#if (defined(ENABLE_KOVPN) || defined(ENABLE_OVPNDCO) || defined(ENABLE_OVPNDCOWIN)) && !defined(OPENVPN_FORCE_TUN_NULL) && !defined(OPENVPN_EXTERNAL_TUN_FACTORY) + if (config.dco) +#if defined(USE_TUN_BUILDER) + dco = DCOTransport::new_controller(config.builder); +#else + dco = DCOTransport::new_controller(nullptr); +#endif +#endif + + layer = cp_main->layer; #ifdef PRIVATE_TUNNEL_PROXY if (config.alt_proxy && !dco) From 1d4334c701952368408c52a8bea9465cefec5a5d Mon Sep 17 00:00:00 2001 From: Lev Stipakov Date: Mon, 2 May 2022 12:37:15 +0300 Subject: [PATCH 022/346] dco-win: support for ChaCha20-Poly1305 Probe cipher support in runtime (works in Windows Server 2022 and Windows 11) and add it to the list of allowed ciphers. White on it, add missing halt check in dco-win client code. Signed-off-by: Lev Stipakov --- openvpn/crypto/cryptoalgs.hpp | 2 +- openvpn/dco/dcocli.hpp | 15 ++++++++++----- openvpn/dco/kocrypto.hpp | 2 +- openvpn/dco/ovpndcowincli.hpp | 9 +++------ test/ovpncli/CMakeLists.txt | 2 ++ 5 files changed, 17 insertions(+), 13 deletions(-) diff --git a/openvpn/crypto/cryptoalgs.hpp b/openvpn/crypto/cryptoalgs.hpp index e7d28d06b..41d0d9990 100644 --- a/openvpn/crypto/cryptoalgs.hpp +++ b/openvpn/crypto/cryptoalgs.hpp @@ -287,7 +287,7 @@ namespace openvpn { } - inline void allow_dc_algs(const std::initializer_list types) + inline void allow_dc_algs(const std::list types) { for (auto& alg : algs) alg.allow_dc(false); diff --git a/openvpn/dco/dcocli.hpp b/openvpn/dco/dcocli.hpp index ebc1efa7b..cd174641e 100644 --- a/openvpn/dco/dcocli.hpp +++ b/openvpn/dco/dcocli.hpp @@ -52,6 +52,7 @@ #include #include #elif ENABLE_OVPNDCOWIN +#include #include #include #else @@ -274,11 +275,15 @@ inline DCO::Ptr new_controller(TunBuilderBase* tb) { if (!OvpnDcoWinClient::available()) return nullptr; - CryptoAlgs::allow_dc_algs({ - CryptoAlgs::AES_128_GCM, - CryptoAlgs::AES_192_GCM, - CryptoAlgs::AES_256_GCM - }); + std::list algs { CryptoAlgs::AES_128_GCM, CryptoAlgs::AES_192_GCM, CryptoAlgs::AES_256_GCM }; + BCRYPT_ALG_HANDLE h; + NTSTATUS status = BCryptOpenAlgorithmProvider(&h, L"CHACHA20_POLY1305", NULL, 0); + if (BCRYPT_SUCCESS(status)) { + BCryptCloseAlgorithmProvider(h, 0); + algs.push_back(CryptoAlgs::CHACHA20_POLY1305); + } + + CryptoAlgs::allow_dc_algs(algs); return ClientConfig::new_controller(nullptr); } inline TransportClient::Ptr diff --git a/openvpn/dco/kocrypto.hpp b/openvpn/dco/kocrypto.hpp index cb3e6ee7f..be1fb7c3f 100644 --- a/openvpn/dco/kocrypto.hpp +++ b/openvpn/dco/kocrypto.hpp @@ -84,8 +84,8 @@ namespace openvpn { case CryptoAlgs::AES_192_CBC: case CryptoAlgs::AES_256_CBC: case CryptoAlgs::BF_CBC: -#ifdef ENABLE_OVPNDCO case CryptoAlgs::CHACHA20_POLY1305: +#ifdef ENABLE_OVPNDCO case CryptoAlgs::NONE: #endif break; diff --git a/openvpn/dco/ovpndcowincli.hpp b/openvpn/dco/ovpndcowincli.hpp index 5654282d0..ba7c9d9cf 100644 --- a/openvpn/dco/ovpndcowincli.hpp +++ b/openvpn/dco/ovpndcowincli.hpp @@ -53,6 +53,8 @@ class OvpnDcoWinClient : public Client, public KoRekey::Receiver { } bool transport_send_const(const Buffer &buf) override { + if (halt) + return false; return send_(buf); } @@ -304,10 +306,6 @@ class OvpnDcoWinClient : public Client, public KoRekey::Receiver { void add_crypto_(const CryptoDCInstance::RekeyType type, const KoRekey::KeyConfig *kc) { - if (kc->cipher_alg != OVPN_CIPHER_ALG_AES_GCM) { - throw ErrorCode(Error::TUN_SETUP_FAILED, true, "unsupported cipher for DCO"); - } - OVPN_CRYPTO_DATA data; ZeroMemory(&data, sizeof(data)); @@ -323,7 +321,7 @@ class OvpnDcoWinClient : public Client, public KoRekey::Receiver { data.KeyId = kc->key_id; data.PeerId = kc->remote_peer_id; - data.CipherAlg = OVPN_CIPHER_ALG::OVPN_CIPHER_ALG_AES_GCM; + data.CipherAlg = (OVPN_CIPHER_ALG)kc->cipher_alg; data.KeySlot = (type == CryptoDCInstance::ACTIVATE_PRIMARY ? OVPN_KEY_SLOT::OVPN_KEY_SLOT_PRIMARY : OVPN_KEY_SLOT::OVPN_KEY_SLOT_SECONDARY); @@ -337,7 +335,6 @@ class OvpnDcoWinClient : public Client, public KoRekey::Receiver { std::ostringstream os; tun_setup_->establish(*po_, Win::module_name(), NULL, os, NULL); OPENVPN_LOG_STRING(os.str()); - } void swap_keys_() { dco_ioctl_(OVPN_IOCTL_SWAP_KEYS); } diff --git a/test/ovpncli/CMakeLists.txt b/test/ovpncli/CMakeLists.txt index 200fb30a7..ba709bdfb 100644 --- a/test/ovpncli/CMakeLists.txt +++ b/test/ovpncli/CMakeLists.txt @@ -48,6 +48,8 @@ if (WIN32) if (${CLI_OVPNDCOWIN}) target_compile_definitions(ovpncliagent PRIVATE ENABLE_OVPNDCOWIN) target_compile_definitions(ovpncli PRIVATE ENABLE_OVPNDCOWIN) + target_link_libraries(ovpncli "bcrypt.lib") + target_link_libraries(ovpncliagent "bcrypt.lib") endif() endif () From 1aa3aae4f8df618ee46834736ec4709c74de664f Mon Sep 17 00:00:00 2001 From: James Yonan Date: Wed, 27 Apr 2022 08:59:16 -0600 Subject: [PATCH 023/346] IP: throw exceptions with OPENVPN_IP_THROW to make it easier to instrument for debugging Also make the error messages for each throw more descriptive by including the function name. Signed-off-by: James Yonan --- openvpn/addr/ip.hpp | 76 ++++++++++++++++++++------------------- openvpn/addr/randaddr.hpp | 2 +- 2 files changed, 40 insertions(+), 38 deletions(-) diff --git a/openvpn/addr/ip.hpp b/openvpn/addr/ip.hpp index bce4e30de..fc626b927 100644 --- a/openvpn/addr/ip.hpp +++ b/openvpn/addr/ip.hpp @@ -35,6 +35,8 @@ #include #include +#define OPENVPN_IP_THROW(ERR) throw openvpn::IP::ip_exception(ERR) + namespace openvpn { // This is our fundamental IP address class that handles IPv4 or IPv6 // IP addresses. It is implemented as a discriminated union of IPv4::Addr @@ -107,10 +109,10 @@ namespace openvpn { openvpn_io::error_code ec; openvpn_io::ip::address a = openvpn_io::ip::make_address(ipstr, ec); if (ec) - throw ip_exception(internal::format_error(ipstr, title, "", ec)); + OPENVPN_IP_THROW(internal::format_error(ipstr, title, "", ec)); const Addr ret = from_asio(a); if (required_version != UNSPEC && required_version != ret.ver) - throw ip_exception(internal::format_error(ipstr, title, version_string_static(required_version), "wrong IP version")); + OPENVPN_IP_THROW(internal::format_error(ipstr, title, version_string_static(required_version), "wrong IP version")); return ret; } @@ -149,7 +151,7 @@ namespace openvpn { void validate_version(const TITLE& title, const Version required_version) const { if (required_version != UNSPEC && required_version != ver) - throw ip_exception(internal::format_error(to_string(), title, version_string_static(required_version), "wrong IP version")); + OPENVPN_IP_THROW(internal::format_error(to_string(), title, version_string_static(required_version), "wrong IP version")); } #else @@ -190,7 +192,7 @@ namespace openvpn { void validate_version(const char *title, Version required_version) const { if (required_version != UNSPEC && required_version != ver) - throw ip_exception(internal::format_error(to_string(), title, version_string_static(required_version), "wrong IP version")); + OPENVPN_IP_THROW(internal::format_error(to_string(), title, version_string_static(required_version), "wrong IP version")); } #ifndef SWIGPYTHON @@ -218,10 +220,10 @@ namespace openvpn { openvpn_io::error_code ec; openvpn_io::ip::address a = openvpn_io::ip::make_address(ipstr, ec); if (ec) - throw ip_exception(internal::format_error(ipstr, title, "", ec)); + OPENVPN_IP_THROW(internal::format_error(ipstr, title, "", ec)); const Addr ret = from_asio(a); if (required_version != UNSPEC && required_version != ret.ver) - throw ip_exception(internal::format_error(ipstr, title, version_string_static(required_version), "wrong IP version")); + OPENVPN_IP_THROW(internal::format_error(ipstr, title, version_string_static(required_version), "wrong IP version")); return ret; } @@ -255,7 +257,7 @@ namespace openvpn { else if (v == V6) return from_ipv6(IPv6::Addr::from_hex(s)); else - throw ip_exception("address unspecified"); + OPENVPN_IP_THROW("from_hex: address unspecified"); } static Addr from_ipv4(IPv4::Addr addr) @@ -279,7 +281,7 @@ namespace openvpn { if (ver == V4) return u.v4; else - throw ip_exception("address is not IPv4"); + OPENVPN_IP_THROW("to_ipv4: address is not IPv4"); } IPv4::Addr to_ipv4_zero() const @@ -289,7 +291,7 @@ namespace openvpn { else if (ver == UNSPEC) return IPv4::Addr::from_zero(); else - throw ip_exception("address is not IPv4 (zero)"); + OPENVPN_IP_THROW("to_ipv4_zero: address is not IPv4"); } const IPv6::Addr& to_ipv6() const @@ -297,7 +299,7 @@ namespace openvpn { if (ver == V6) return u.v6; else - throw ip_exception("address is not IPv6"); + OPENVPN_IP_THROW("to_ipv6: address is not IPv6"); } IPv6::Addr to_ipv6_zero() const @@ -307,7 +309,7 @@ namespace openvpn { else if (ver == UNSPEC) return IPv6::Addr::from_zero(); else - throw ip_exception("address is not IPv6 (zero)"); + OPENVPN_IP_THROW("to_ipv6_zero: address is not IPv6"); } const IPv4::Addr& to_ipv4_nocheck() const @@ -342,7 +344,7 @@ namespace openvpn { else if (v == V6) return from_ipv6(IPv6::Addr::from_ulong(ul)); else - throw ip_exception("address unspecified"); + OPENVPN_IP_THROW("from_ulong: address unspecified"); } // return *this as a ulong, will raise exception on overflow @@ -353,7 +355,7 @@ namespace openvpn { else if (ver == V6) return u.v6.to_ulong(); else - throw ip_exception("address unspecified"); + OPENVPN_IP_THROW("to_ulong: address unspecified"); } static Addr from_long(Version v, long ul) @@ -363,7 +365,7 @@ namespace openvpn { else if (v == V6) return from_ipv6(IPv6::Addr::from_long(ul)); else - throw ip_exception("address unspecified"); + OPENVPN_IP_THROW("from_long: address unspecified"); } // return *this as a long, will raise exception on overflow @@ -374,7 +376,7 @@ namespace openvpn { else if (ver == V6) return u.v6.to_long(); else - throw ip_exception("address unspecified"); + OPENVPN_IP_THROW("to_long: address unspecified"); } // return Addr from 16 byte binary string @@ -413,7 +415,7 @@ namespace openvpn { else if (ver == V6) u.v6.to_byte_string(bytestr); else - throw ip_exception("address unspecified"); + OPENVPN_IP_THROW("to_byte_string_variable: address unspecified"); } std::uint32_t to_uint32_net() const // return value in net byte order @@ -432,7 +434,7 @@ namespace openvpn { else if (v == V6) return from_ipv6(IPv6::Addr::from_zero()); else - throw ip_exception("address unspecified"); + OPENVPN_IP_THROW("from_zero: address unspecified"); } // construct an address where all bits are zero @@ -443,7 +445,7 @@ namespace openvpn { else if (v == V6) return from_ipv6(IPv6::Addr::from_one()); else - throw ip_exception("address unspecified"); + OPENVPN_IP_THROW("from_one: address unspecified"); } // construct an address where all bits are one @@ -454,7 +456,7 @@ namespace openvpn { else if (v == V6) return from_ipv6(IPv6::Addr::from_zero_complement()); else - throw ip_exception("address unspecified"); + OPENVPN_IP_THROW("from_zero_complement: address unspecified"); } // validate the prefix length for the IP version @@ -481,7 +483,7 @@ namespace openvpn { else if (v == V6) return from_ipv6(IPv6::Addr::netmask_from_prefix_len(prefix_len)); else - throw ip_exception("address unspecified"); + OPENVPN_IP_THROW("netmask_from_prefix_len: address unspecified"); } // build a netmask using *this as extent @@ -492,7 +494,7 @@ namespace openvpn { else if (ver == V6) return from_ipv6(u.v6.netmask_from_extent()); else - throw ip_exception("address unspecified"); + OPENVPN_IP_THROW("netmask_from_extent: address unspecified"); } std::string to_string() const @@ -525,7 +527,7 @@ namespace openvpn { else if (ver == V6) return u.v6.to_hex(); else - throw ip_exception("address unspecified"); + OPENVPN_IP_THROW("to_hex: address unspecified"); } std::string arpa() const @@ -535,7 +537,7 @@ namespace openvpn { else if (ver == V6) return u.v6.arpa(); else - throw ip_exception("address unspecified"); + OPENVPN_IP_THROW("arpa: address unspecified"); } static Addr from_asio(const openvpn_io::ip::address& addr) @@ -555,7 +557,7 @@ namespace openvpn { return a; } else - throw ip_exception("address unspecified"); + OPENVPN_IP_THROW("from_asio: address unspecified"); } openvpn_io::ip::address to_asio() const @@ -567,7 +569,7 @@ namespace openvpn { case V6: return openvpn_io::ip::address_v6(u.v6.to_asio()); default: - throw ip_exception("address unspecified"); + OPENVPN_IP_THROW("to_asio: address unspecified"); } } @@ -589,7 +591,7 @@ namespace openvpn { return ret; } default: - throw ip_exception("address unspecified"); + OPENVPN_IP_THROW("operator+: address unspecified"); } } @@ -600,7 +602,7 @@ namespace openvpn { #define OPENVPN_IP_OPERATOR_BINOP(OP) \ Addr operator OP (const Addr& other) const { \ if (ver != other.ver) \ - throw ip_exception("version inconsistency"); \ + OPENVPN_IP_THROW("binop: version inconsistency"); \ switch (ver) \ { \ case V4: \ @@ -618,7 +620,7 @@ namespace openvpn { return ret; \ } \ default: \ - throw ip_exception("address unspecified"); \ + OPENVPN_IP_THROW("binop: address unspecified"); \ } \ } @@ -650,7 +652,7 @@ namespace openvpn { return ret; } default: - throw ip_exception("address unspecified"); + OPENVPN_IP_THROW("operator<<: address unspecified"); } } @@ -672,7 +674,7 @@ namespace openvpn { return ret; } default: - throw ip_exception("address unspecified"); + OPENVPN_IP_THROW("operator>>: address unspecified"); } } @@ -694,7 +696,7 @@ namespace openvpn { return ret; } default: - throw ip_exception("address unspecified"); + OPENVPN_IP_THROW("operator~: address unspecified"); } } @@ -716,7 +718,7 @@ namespace openvpn { return ret; } default: - throw ip_exception("address unspecified"); + OPENVPN_IP_THROW("network_addr: address unspecified"); } } @@ -872,7 +874,7 @@ namespace openvpn { case V6: return 1; default: - throw ip_exception("version index undefined"); + OPENVPN_IP_THROW("version_index: version index undefined"); } } @@ -902,7 +904,7 @@ namespace openvpn { void verify_version_consistency(const Addr& other) const { if (!is_compatible(other)) - throw ip_exception("version inconsistency"); + OPENVPN_IP_THROW("verify_version_consistency: version inconsistency"); } // throw exception if address is not a valid netmask @@ -922,7 +924,7 @@ namespace openvpn { case V6: return u.v6.prefix_len(); default: - throw ip_exception("address unspecified"); + OPENVPN_IP_THROW("prefix_len: address unspecified"); } } @@ -942,7 +944,7 @@ namespace openvpn { case V6: return u.v6.host_len(); default: - throw ip_exception("address unspecified"); + OPENVPN_IP_THROW("host_len: address unspecified"); } } @@ -956,7 +958,7 @@ namespace openvpn { case V6: return from_ipv6(u.v6.extent_from_netmask()); default: - throw ip_exception("address unspecified"); + OPENVPN_IP_THROW("extent_from_netmask: address unspecified"); } } diff --git a/openvpn/addr/randaddr.hpp b/openvpn/addr/randaddr.hpp index d136223bf..7708757dd 100644 --- a/openvpn/addr/randaddr.hpp +++ b/openvpn/addr/randaddr.hpp @@ -49,7 +49,7 @@ namespace openvpn { case Addr::V6: return Addr::from_ipv6(random_addr_v6(prng)); default: - throw ip_exception("address unspecified"); + OPENVPN_IP_THROW("random_addr: address unspecified"); } } From d617b97932e566a4b75389b6596a495ceb1b0d81 Mon Sep 17 00:00:00 2001 From: James Yonan Date: Wed, 27 Apr 2022 09:32:15 -0600 Subject: [PATCH 024/346] AsioBoundSocket: support concurrent use of IPv4 and IPv6 local addresses Signed-off-by: James Yonan --- openvpn/asio/asioboundsock.hpp | 90 +++++++++++++++++++++++++++------- 1 file changed, 73 insertions(+), 17 deletions(-) diff --git a/openvpn/asio/asioboundsock.hpp b/openvpn/asio/asioboundsock.hpp index bc0752771..c647c5aae 100644 --- a/openvpn/asio/asioboundsock.hpp +++ b/openvpn/asio/asioboundsock.hpp @@ -45,24 +45,41 @@ namespace openvpn { { } - // if port 0, kernel will dynamically allocate free port + // May be called twice with IPv4 and IPv6 address. + // If port 0, kernel will dynamically allocate free port. void bind_local(const IP::Addr& addr, const unsigned short port=0) { - bind_local_addr = addr; - bind_local_port = port; + switch (addr.version()) + { + case IP::Addr::V4: + v4.bind_local(addr.to_ipv4(), port); + break; + case IP::Addr::V6: + v6.bind_local(addr.to_ipv6(), port); + break; + default: + return; + } } std::string to_string() const { std::string ret; ret.reserve(64); - if (bind_local_addr.defined()) + + if (v4.defined()) + { + ret += "local4="; + ret += v4.to_string(); + } + if (v6.defined()) { - ret += "local=["; - ret += bind_local_addr.to_string(); - ret += "]:"; - ret += openvpn::to_string(bind_local_port); + if (!ret.empty()) + ret += ' '; + ret += "local6="; + ret += v6.to_string(); } + try { const std::string re = openvpn::to_string(remote_endpoint()); if (!ret.empty()) @@ -77,20 +94,59 @@ namespace openvpn { } protected: + template + class Proto + { + public: + Proto() + { + local_.zero(); + port_ = 0; + } + + bool defined() const + { + return local_.specified(); + } + + void bind_local(const IP_ADDR& local, const unsigned short port) + { + local_ = local; + port_ = port; + } + + template + void post_open(PARENT* parent, openvpn_io::error_code& ec) const + { + if (local_.specified()) + { + parent->set_option(openvpn_io::socket_base::reuse_address(true), ec); + if (!ec) + parent->bind(openvpn_io::ip::tcp::endpoint(local_.to_asio(), port_), ec); + } + } + + std::string to_string() const + { + return '[' + local_.to_string() + "]:" + std::to_string(port_); + } + + private: + IP_ADDR local_; + unsigned short port_; + }; + virtual void async_connect_post_open(const protocol_type& protocol, openvpn_io::error_code& ec) override { - if (bind_local_addr.defined()) - { - set_option(openvpn_io::socket_base::reuse_address(true), ec); - if (ec) - return; - bind(openvpn_io::ip::tcp::endpoint(bind_local_addr.to_asio(), bind_local_port), ec); - } + if (protocol == openvpn_io::ip::tcp::v4()) + v4.post_open(this, ec); + else if (protocol == openvpn_io::ip::tcp::v6()) + v6.post_open(this, ec); } private: - IP::Addr bind_local_addr; - unsigned short bind_local_port = 0; + Proto v4; + Proto v6; }; } From b0a99915bb9b6a6ef5ce080bb315d8d46f254bd8 Mon Sep 17 00:00:00 2001 From: James Yonan Date: Wed, 27 Apr 2022 09:35:12 -0600 Subject: [PATCH 025/346] AsioPolySock: make alt_routing_enabled() a const method Signed-off-by: James Yonan --- openvpn/asio/asiopolysock.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openvpn/asio/asiopolysock.hpp b/openvpn/asio/asiopolysock.hpp index 9c7784e97..d058a6c2a 100644 --- a/openvpn/asio/asiopolysock.hpp +++ b/openvpn/asio/asiopolysock.hpp @@ -90,7 +90,7 @@ namespace openvpn { #endif #if defined(OPENVPN_POLYSOCK_SUPPORTS_ALT_ROUTING) - virtual bool alt_routing_enabled() + virtual bool alt_routing_enabled() const { return false; } @@ -214,7 +214,7 @@ namespace openvpn { return proto + socket.to_string(); } - virtual bool alt_routing_enabled() override + virtual bool alt_routing_enabled() const override { return socket.alt_routing_enabled(); } From b8bc2988adcbea92fd3029824b8314cc1d76658d Mon Sep 17 00:00:00 2001 From: James Yonan Date: Wed, 27 Apr 2022 11:07:26 -0600 Subject: [PATCH 026/346] IP::Route: break out prefix length validation into standalone methods Signed-off-by: James Yonan --- openvpn/addr/route.hpp | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/openvpn/addr/route.hpp b/openvpn/addr/route.hpp index bc58eb308..037d77934 100644 --- a/openvpn/addr/route.hpp +++ b/openvpn/addr/route.hpp @@ -86,8 +86,7 @@ namespace openvpn { if (pair.size() >= 2) { r.prefix_len = parse_number_throw(pair[1], "prefix length"); - if (r.prefix_len > r.addr.size()) - OPENVPN_THROW(route_error, (!StringTempl::empty(title) ? title : "route") << " : bad prefix length : " << rtstr); + r.validate_prefix_length(title); } else r.prefix_len = r.addr.size(); @@ -99,6 +98,13 @@ namespace openvpn { return from_string(rtstr, nullptr); } + template + void validate_prefix_length(const TITLE& title) + { + if (!is_valid()) + OPENVPN_THROW(route_error, (!StringTempl::empty(title) ? title : "route") << ' ' << addr.to_string() << " : bad prefix length : " << prefix_len); + } + #else RouteType(const std::string& rtstr, const char *title = nullptr) @@ -121,14 +127,19 @@ namespace openvpn { if (pair.size() >= 2) { r.prefix_len = parse_number_throw(pair[1], "prefix length"); - if (r.prefix_len > r.addr.size()) - OPENVPN_THROW(route_error, (title ? title : "route") << " : bad prefix length : " << rtstr); + r.validate_prefix_length(title); } else r.prefix_len = r.addr.size(); return r; } + void validate_prefix_length(const char *title = nullptr) + { + if (!is_valid()) + OPENVPN_THROW(route_error, (title ? title : "route") << ' ' << addr.to_string() << " : bad prefix length : " << prefix_len); + } + #endif bool defined() const @@ -171,6 +182,11 @@ namespace openvpn { return (addr & netmask()) == addr; } + bool is_valid() const + { + return prefix_len <= addr.size(); + } + void force_canonical() { addr = addr & netmask(); From c6e47099b792be37af9c3f84a6e9e5099e7a0c6e Mon Sep 17 00:00:00 2001 From: James Yonan Date: Wed, 27 Apr 2022 09:39:17 -0600 Subject: [PATCH 027/346] SITNL: allow including programs to define their own version of OPENVPN_LOG_RTNL() Signed-off-by: James Yonan --- openvpn/tun/linux/client/sitnl.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openvpn/tun/linux/client/sitnl.hpp b/openvpn/tun/linux/client/sitnl.hpp index be10a9fbb..6927ae3fc 100644 --- a/openvpn/tun/linux/client/sitnl.hpp +++ b/openvpn/tun/linux/client/sitnl.hpp @@ -36,11 +36,13 @@ #include +#ifndef OPENVPN_LOG_RTNL #ifdef DEBUG_RTNL #define OPENVPN_LOG_RTNL(_x) OPENVPN_LOG(_x) #else #define OPENVPN_LOG_RTNL(_x) #endif +#endif namespace openvpn { namespace TunNetlink { From 2d2758b7a8ee4554951f76bfa254978b3faf1887 Mon Sep 17 00:00:00 2001 From: James Yonan Date: Wed, 27 Apr 2022 11:00:57 -0600 Subject: [PATCH 028/346] SITNL: use cleaner and more performant IP::Route construction When constructing an IP::Route from netlink raw data, avoid round-trip conversion to string and back. Signed-off-by: James Yonan --- openvpn/tun/linux/client/sitnl.hpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openvpn/tun/linux/client/sitnl.hpp b/openvpn/tun/linux/client/sitnl.hpp index 6927ae3fc..5e72a7748 100644 --- a/openvpn/tun/linux/client/sitnl.hpp +++ b/openvpn/tun/linux/client/sitnl.hpp @@ -458,10 +458,12 @@ namespace openvpn { switch (res->family) { case AF_INET: - route = IP::Route(IPv4::Addr::from_bytes_net(bytestr).to_string() + "/" + std::to_string(r->rtm_dst_len)); + route = IP::Route(IP::Addr::from_ipv4(IPv4::Addr::from_bytes_net(bytestr)), r->rtm_dst_len); + route.validate_prefix_length("SITNL route4"); break; case AF_INET6: - route = IP::Route(IPv6::Addr::from_byte_string(bytestr).to_string() + "/" + std::to_string(r->rtm_dst_len)); + route = IP::Route(IP::Addr::from_ipv6(IPv6::Addr::from_byte_string(bytestr)), r->rtm_dst_len); + route.validate_prefix_length("SITNL route6"); break; } } From 68854ea916639e7ed3c3e770cff26e96c1bef050 Mon Sep 17 00:00:00 2001 From: James Yonan Date: Wed, 27 Apr 2022 11:02:28 -0600 Subject: [PATCH 029/346] SITNL: added new OPENVPN_LOG_RTNL() call to better understand netlink representation of route list Signed-off-by: James Yonan --- openvpn/tun/linux/client/sitnl.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openvpn/tun/linux/client/sitnl.hpp b/openvpn/tun/linux/client/sitnl.hpp index 5e72a7748..00ad22fb1 100644 --- a/openvpn/tun/linux/client/sitnl.hpp +++ b/openvpn/tun/linux/client/sitnl.hpp @@ -491,6 +491,8 @@ namespace openvpn { rta = RTA_NEXT(rta, len); } + OPENVPN_LOG_RTNL(__func__ << ": ROUTE " << route.to_string() << " metric=" << metric << " ifindex=" << ifindex << " proto=" << int(r->rtm_protocol) << " scope=" << int(r->rtm_scope) << " type=" << int(r->rtm_type) << " table=" << int(r->rtm_table)); + if (!gw.defined() || ifindex <= 0) { return 0; From 6f10dfdce946b3c79e9691dbbb076950f66cf0c8 Mon Sep 17 00:00:00 2001 From: James Yonan Date: Wed, 27 Apr 2022 11:04:24 -0600 Subject: [PATCH 030/346] SITNL: added net_iface_addr() to return the primary address/prefix_len on a given interface Signed-off-by: James Yonan --- openvpn/tun/linux/client/sitnl.hpp | 115 +++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) diff --git a/openvpn/tun/linux/client/sitnl.hpp b/openvpn/tun/linux/client/sitnl.hpp index 00ad22fb1..4e8deebbd 100644 --- a/openvpn/tun/linux/client/sitnl.hpp +++ b/openvpn/tun/linux/client/sitnl.hpp @@ -610,6 +610,99 @@ namespace openvpn { return ret; } + /* state info for sitnl_iface_addr_save() */ + typedef struct + { + sa_family_t family; + __u32 ifindex; + IP::Route route; + } iface_addr_res_t; + + static int + sitnl_iface_addr_save(struct nlmsghdr *n, void *arg) + { + iface_addr_res_t *res = (iface_addr_res_t *)arg; + struct ifaddrmsg *ifa = (struct ifaddrmsg *)NLMSG_DATA(n); + struct rtattr *rta = IFA_RTA(ifa); + int len = n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)); + IP::Route route; + struct ifaddrmsg save = {}; + + while (RTA_OK(rta, len)) + { + switch (rta->rta_type) + { + case IFA_ADDRESS: + /* interface address */ + { + const unsigned char *bytestr = (unsigned char *)RTA_DATA(rta); + switch (res->family) + { + case AF_INET: + route = IP::Route(IP::Addr::from_ipv4(IPv4::Addr::from_bytes_net(bytestr)), ifa->ifa_prefixlen); + save = *ifa; + OPENVPN_LOG_RTNL(__func__ << ": ADD4 " << route.to_string() << " family=" << int(ifa->ifa_family) << " prefixlen=" << int(ifa->ifa_prefixlen) << " flags=" << int(ifa->ifa_flags) << " scope=" << int(ifa->ifa_scope) << " index=" << int(ifa->ifa_index)); + break; + case AF_INET6: + route = IP::Route(IP::Addr::from_ipv6(IPv6::Addr::from_byte_string(bytestr)), ifa->ifa_prefixlen); + save = *ifa; + OPENVPN_LOG_RTNL(__func__ << ": ADDR6 " << route.to_string() << " family=" << int(ifa->ifa_family) << " prefixlen=" << int(ifa->ifa_prefixlen) << " flags=" << int(ifa->ifa_flags) << " scope=" << int(ifa->ifa_scope) << " index=" << int(ifa->ifa_index)); + break; + } + } + break; + } + + rta = RTA_NEXT(rta, len); + } + + if (!res->route.defined() && save.ifa_index == res->ifindex) + { + res->route = route; + OPENVPN_LOG_RTNL(__func__ << ": MATCH " << route.to_string() << " ifindex=" << save.ifa_index); + } + return 0; + } + + /** + * Return interface primary address/netmask given interface index. + * @param ifindex interface to probe + * @param family address family + * @param [out] address/netmask of interface + * @return success if 0, error if < 0 + */ + static int + sitnl_iface_addr(const int ifindex, + const int family, + IP::Route& route) + { + struct sitnl_route_req req = { }; + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.r)); + req.n.nlmsg_type = RTM_GETADDR; + req.n.nlmsg_flags = NLM_F_REQUEST; + + iface_addr_res_t res; + res.ifindex = ifindex; + res.family = req.r.rtm_family = family; + + req.n.nlmsg_flags |= NLM_F_DUMP; + + const int ret = sitnl_send(&req.n, 0, 0, sitnl_iface_addr_save, &res); + if (ret >= 0 && res.route.defined()) + { + /* save result in output variables */ + route = res.route; + + OPENVPN_LOG(__func__ << " result: route " << res.route.to_string() << " ifindex=" << res.ifindex); + } + else + { + OPENVPN_LOG(__func__ << ": failed to retrieve addr, err=" << ret); + } + + return ret; + } + static int sitnl_addr_set(const int cmd, const uint32_t flags, const std::string& iface, const IP::Addr& local, const IP::Addr& remote, int prefixlen, @@ -845,6 +938,28 @@ namespace openvpn { return ret; } + /** + * @brief Get interface address/netmask + * + * @param iface interface to probe + * @param family address family + * @return interface primary address/subnet or undefined route if error + */ + static IP::Route + net_iface_addr(const std::string& iface, + const int family) + { + unsigned int ifindex = if_nametoindex(iface.c_str()); + if (ifindex) + { + IP::Route route; + const int ret = sitnl_iface_addr(ifindex, family, route); + if (ret == 0) + return route; + } + return IP::Route(); + } + /** * @brief Add new interface (similar to ip link add) * From 79b6bd15e00a68702917b2cb764abe6285392fe2 Mon Sep 17 00:00:00 2001 From: James Yonan Date: Wed, 27 Apr 2022 11:10:20 -0600 Subject: [PATCH 031/346] WS::ClientSet: check io_context stopped() method before running or persisting Signed-off-by: James Yonan --- openvpn/ws/httpcliset.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openvpn/ws/httpcliset.hpp b/openvpn/ws/httpcliset.hpp index 0bace9838..7a26688c3 100644 --- a/openvpn/ws/httpcliset.hpp +++ b/openvpn/ws/httpcliset.hpp @@ -543,7 +543,7 @@ namespace openvpn { cs->new_request(ts); if (sps) { - while (cs->clients.size()) + while (cs->clients.size() && !io_context->stopped()) io_context->run_one(); } else @@ -556,7 +556,7 @@ namespace openvpn { io_context->poll(); // execute completion handlers throw; } - if (sps) + if (sps && !io_context->stopped()) ts->hsc.persist_io_context(std::move(io_context)); } From 42e586417fe4bf09226e4613fda31360a46e8d16 Mon Sep 17 00:00:00 2001 From: James Yonan Date: Wed, 27 Apr 2022 11:14:31 -0600 Subject: [PATCH 032/346] WS::ClientSet: added commented-out OPENVPN_LOG line for showing HTTPStateContainer state Signed-off-by: James Yonan --- openvpn/ws/httpcliset.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/openvpn/ws/httpcliset.hpp b/openvpn/ws/httpcliset.hpp index 7a26688c3..8203f4251 100644 --- a/openvpn/ws/httpcliset.hpp +++ b/openvpn/ws/httpcliset.hpp @@ -742,6 +742,7 @@ namespace openvpn { ts->error_recovery->retry(*ts, t); // init and attach HTTPStateContainer + //OPENVPN_LOG("******* HTTPStateContainer alive=" << ts->alive() << " error_retry=" << error_retry << " n_clients=" << parent->clients.size()); if (!ts->alive()) ts->hsc.construct(parent->io_context, ts->http_config); ts->hsc.attach(this); From b0e1c2ec6ed36ea093969dae0147256fc808efff Mon Sep 17 00:00:00 2001 From: James Yonan Date: Wed, 27 Apr 2022 11:15:44 -0600 Subject: [PATCH 033/346] WS::Creds: added password_eq() method Signed-off-by: James Yonan --- openvpn/ws/httpcreds.hpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/openvpn/ws/httpcreds.hpp b/openvpn/ws/httpcreds.hpp index bffc29350..b6af10906 100644 --- a/openvpn/ws/httpcreds.hpp +++ b/openvpn/ws/httpcreds.hpp @@ -150,6 +150,13 @@ namespace openvpn { return neq; } + bool password_eq(const Creds& rhs) const + { + bool neq = crypto::str_neq(password, rhs.password); + atomic_thread_fence(std::memory_order_acq_rel); + return !neq; + } + std::string to_string() const { return username + '/' + password; From 15e8dcc86da2dda867f79675c6acaba03037cadf Mon Sep 17 00:00:00 2001 From: James Yonan Date: Thu, 28 Apr 2022 08:12:06 -0600 Subject: [PATCH 034/346] SetUserGroupRetainCap: added a new constructor for user/group parameters provided as const char * This follows the organization of SetUserGroup (the base class), and solves a potential null pointer dereference issue cited by Coverity. Signed-off-by: James Yonan --- openvpn/linux/usergroup_retain_cap.hpp | 27 ++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/openvpn/linux/usergroup_retain_cap.hpp b/openvpn/linux/usergroup_retain_cap.hpp index 70f64d2d1..26f4d2f9b 100644 --- a/openvpn/linux/usergroup_retain_cap.hpp +++ b/openvpn/linux/usergroup_retain_cap.hpp @@ -49,12 +49,17 @@ namespace openvpn { : SetUserGroup(user, group, strict), retain_caps(retain_caps_arg) { - // get full root privileges - if (::setresuid(0, 0, 0)) - { - const int eno = errno; - OPENVPN_THROW(user_group_err, "SetUserGroupRetainCap setresuid root fail: " << strerror_str(eno)); - } + grab_root(); + } + + SetUserGroupRetainCap(const char *user, + const char *group, + const bool strict, + std::initializer_list retain_caps_arg) + : SetUserGroup(user, group, strict), + retain_caps(retain_caps_arg) + { + grab_root(); } // call first in all threads before user/group downgrade @@ -183,6 +188,16 @@ namespace openvpn { const std::string title; }; + void grab_root() + { + // get full root privileges + if (::setresuid(0, 0, 0)) + { + const int eno = errno; + OPENVPN_THROW(user_group_err, "SetUserGroupRetainCap setresuid root fail: " << strerror_str(eno)); + } + } + const std::vector retain_caps; }; From 5392a9e1976f54af09fec97e658dc7dda4f2f021 Mon Sep 17 00:00:00 2001 From: James Yonan Date: Thu, 5 May 2022 12:39:38 -0600 Subject: [PATCH 035/346] Listen::List: use opt.get_index_ptr() optimization to avoid linear search through option list This removes the feature of allowing directive to be a prefix, but I didn't find any users of this method that require it. Signed-off-by: James Yonan --- openvpn/server/listenlist.hpp | 171 +++++++++++++++------------------- 1 file changed, 73 insertions(+), 98 deletions(-) diff --git a/openvpn/server/listenlist.hpp b/openvpn/server/listenlist.hpp index 0362206cc..2714aaf79 100644 --- a/openvpn/server/listenlist.hpp +++ b/openvpn/server/listenlist.hpp @@ -121,106 +121,96 @@ namespace openvpn { const LoadMode load_mode, const unsigned int n_cores) { - size_t n_listen = 0; - - for (auto &o : opt) + const auto* opt_list = opt.get_index_ptr(directive); + if (opt_list) { - if (match(directive, o)) - ++n_listen; - } + reserve(opt_list->size()); + for (auto i : *opt_list) + { + const Option& o = opt[i]; + o.touch(); - if (n_listen) - { - reserve(n_listen); + unsigned int mult = 1; + unsigned int local = 0; - for (auto &o : opt) - { - if (match(directive, o)) - { - o.touch(); + Item e; - unsigned int mult = 1; - unsigned int local = 0; + // directive + e.directive = o.get(0, 64); - Item e; + // IP address + e.addr = o.get(1, 128); - // directive - e.directive = o.get(0, 64); + // port number + e.port = o.get(2, 16); + if (Protocol::is_local_type(e.port)) + { + local = 1; + e.port = ""; + } + else + HostPort::validate_port(e.port, e.directive); - // IP address - e.addr = o.get(1, 128); + // protocol + { + const std::string title = e.directive + " protocol"; + e.proto = Protocol::parse(o.get(3-local, 16), Protocol::NO_SUFFIX, title.c_str()); + } + if (!local) + { + // modify protocol based on IP version of given address + const std::string title = e.directive + " addr"; + const IP::Addr addr = IP::Addr(e.addr, title.c_str()); + e.proto.mod_addr_version(addr.version()); + } - // port number - e.port = o.get(2, 16); - if (Protocol::is_local_type(e.port)) - { - local = 1; - e.port = ""; - } - else - HostPort::validate_port(e.port, e.directive); - - // protocol - { - const std::string title = e.directive + " protocol"; - e.proto = Protocol::parse(o.get(3-local, 16), Protocol::NO_SUFFIX, title.c_str()); - } - if (!local) + // number of threads + int n_threads_exists = 0; + { + const std::string ntstr = o.get_optional(4-local, 16); + if (ntstr.length() > 0 && string::is_digit(ntstr[0])) + n_threads_exists = 1; + } + if (n_threads_exists) + { + std::string n_threads = o.get(4-local, 16); + if (string::ends_with(n_threads, "*N")) { - // modify protocol based on IP version of given address - const std::string title = e.directive + " addr"; - const IP::Addr addr = IP::Addr(e.addr, title.c_str()); - e.proto.mod_addr_version(addr.version()); + mult = n_cores; + n_threads = n_threads.substr(0, n_threads.length() - 2); } - - // number of threads - int n_threads_exists = 0; - { - const std::string ntstr = o.get_optional(4-local, 16); - if (ntstr.length() > 0 && string::is_digit(ntstr[0])) - n_threads_exists = 1; - } - if (n_threads_exists) - { - std::string n_threads = o.get(4-local, 16); - if (string::ends_with(n_threads, "*N")) - { - mult = n_cores; - n_threads = n_threads.substr(0, n_threads.length() - 2); - } - if (!parse_number_validate(n_threads, 3, 1, 100, &e.n_threads)) - OPENVPN_THROW(option_error, e.directive << ": bad num threads: " << n_threads); + if (!parse_number_validate(n_threads, 3, 1, 100, &e.n_threads)) + OPENVPN_THROW(option_error, e.directive << ": bad num threads: " << n_threads); #ifndef OPENVPN_PLATFORM_WIN - if (local && e.n_threads != 1) - OPENVPN_THROW(option_error, e.directive << ": local socket only supports one thread per pathname (not " << n_threads << ')'); + if (local && e.n_threads != 1) + OPENVPN_THROW(option_error, e.directive << ": local socket only supports one thread per pathname (not " << n_threads << ')'); #endif - e.n_threads *= mult; - } - else - e.n_threads = 1; + e.n_threads *= mult; + } + else + e.n_threads = 1; - // SSL - if (o.size() >= 5-local+n_threads_exists) + // SSL + if (o.size() >= 5-local+n_threads_exists) + { + const std::string& ssl_qualifier = o.get(4-local+n_threads_exists, 16); + if (ssl_qualifier == "ssl") { - const std::string& ssl_qualifier = o.get(4-local+n_threads_exists, 16); - if (ssl_qualifier == "ssl") - { - if (local) - OPENVPN_THROW(option_error, e.directive << ": SSL not supported on local sockets"); - e.ssl = Item::SSLOn; - } - else if (ssl_qualifier == "!ssl") - e.ssl = Item::SSLOff; + if (local) + OPENVPN_THROW(option_error, e.directive << ": SSL not supported on local sockets"); + e.ssl = Item::SSLOn; + } + else if (ssl_qualifier == "!ssl") + e.ssl = Item::SSLOff; #ifdef OPENVPN_POLYSOCK_SUPPORTS_ALT_ROUTING - else if (ssl_qualifier == "alt") - e.ssl = Item::AltRouting; + else if (ssl_qualifier == "alt") + e.ssl = Item::AltRouting; #endif - else - OPENVPN_THROW(option_error, e.directive << ": unrecognized SSL qualifier"); - } - - push_back(std::move(e)); + else + OPENVPN_THROW(option_error, e.directive << ": unrecognized SSL qualifier"); } + + push_back(std::move(e)); } } else if (load_mode == AllowDefault) @@ -323,21 +313,6 @@ namespace openvpn { ret.emplace_back(e.port_offset(unit)); return ret; } - - private: - static bool match(const std::string& directive, const Option& o) - { - const size_t len = directive.length(); - if (len && o.size()) - { - if (directive[len-1] == '-') - return string::starts_with(o.ref(0), directive); - else - return o.ref(0) == directive; - } - else - return false; - } }; } } From b48ce41fa10d894a04fc00e12bcdafc39cb200b6 Mon Sep 17 00:00:00 2001 From: James Yonan Date: Fri, 13 May 2022 09:48:24 -0600 Subject: [PATCH 036/346] IP::Addr: minor fixes * Use const Version where appropriate. * Fixed some incorrect commments. * When converting IP version to string, use "UNSPEC" for unspecified version. Signed-off-by: James Yonan --- openvpn/addr/ip.hpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/openvpn/addr/ip.hpp b/openvpn/addr/ip.hpp index fc626b927..7afde4efc 100644 --- a/openvpn/addr/ip.hpp +++ b/openvpn/addr/ip.hpp @@ -427,36 +427,36 @@ namespace openvpn { } // construct an address where all bits are zero - static Addr from_zero(Version v) + static Addr from_zero(const Version v) { if (v == V4) return from_ipv4(IPv4::Addr::from_zero()); else if (v == V6) return from_ipv6(IPv6::Addr::from_zero()); else - OPENVPN_IP_THROW("from_zero: address unspecified"); + OPENVPN_IP_THROW("from_zero: IP version unspecified"); } - // construct an address where all bits are zero - static Addr from_one(Version v) + // construct the "one" address + static Addr from_one(const Version v) { if (v == V4) return from_ipv4(IPv4::Addr::from_one()); else if (v == V6) return from_ipv6(IPv6::Addr::from_one()); else - OPENVPN_IP_THROW("from_one: address unspecified"); + OPENVPN_IP_THROW("from_one: IP version unspecified"); } // construct an address where all bits are one - static Addr from_zero_complement(Version v) + static Addr from_zero_complement(const Version v) { if (v == V4) return from_ipv4(IPv4::Addr::from_zero_complement()); else if (v == V6) return from_ipv6(IPv6::Addr::from_zero_complement()); else - OPENVPN_IP_THROW("from_zero_complement: address unspecified"); + OPENVPN_IP_THROW("from_zero_complement: IP version unspecified"); } // validate the prefix length for the IP version @@ -841,7 +841,7 @@ namespace openvpn { case V6: return "v6"; default: - return "v?"; + return "UNSPEC"; } } From 5c16cfa255e9500f83c199f7c9eb3f2feea6a089 Mon Sep 17 00:00:00 2001 From: James Yonan Date: Fri, 13 May 2022 10:07:38 -0600 Subject: [PATCH 037/346] IP::Route: added canonical_addr() method and refactored some other methods to use it Signed-off-by: James Yonan --- openvpn/addr/route.hpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/openvpn/addr/route.hpp b/openvpn/addr/route.hpp index 037d77934..41bd4d839 100644 --- a/openvpn/addr/route.hpp +++ b/openvpn/addr/route.hpp @@ -179,7 +179,7 @@ namespace openvpn { bool is_canonical() const { - return (addr & netmask()) == addr; + return canonical_addr() == addr; } bool is_valid() const @@ -187,9 +187,14 @@ namespace openvpn { return prefix_len <= addr.size(); } + ADDR canonical_addr() const + { + return addr & netmask(); + } + void force_canonical() { - addr = addr & netmask(); + addr = canonical_addr(); } void verify_canonical() const From f1a1776eee5bf168e519f850b31da8a6b2e949fe Mon Sep 17 00:00:00 2001 From: James Yonan Date: Fri, 13 May 2022 10:25:54 -0600 Subject: [PATCH 038/346] AWS::api_ca(): use file_exists() function to test for file existence Signed-off-by: James Yonan --- openvpn/aws/awsca.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openvpn/aws/awsca.hpp b/openvpn/aws/awsca.hpp index 1181d1c28..769d4ebaf 100644 --- a/openvpn/aws/awsca.hpp +++ b/openvpn/aws/awsca.hpp @@ -24,6 +24,7 @@ #pragma once #include +#include namespace openvpn { namespace AWS { @@ -40,7 +41,7 @@ namespace openvpn { }; for (const auto& cert : certs) { - if (std::ifstream{cert}) + if (file_exists(cert)) return read_text_unix(cert); } throw file_unix_error("No CA certificate files found in system paths"); From 96d6d487beb5c7f6b9b67bdf41dbd2388d816425 Mon Sep 17 00:00:00 2001 From: James Yonan Date: Fri, 13 May 2022 10:27:39 -0600 Subject: [PATCH 039/346] AWS::Creds: added a template constructor to allow initialization from other kinds of credential classes Signed-off-by: James Yonan --- openvpn/aws/awscreds.hpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/openvpn/aws/awscreds.hpp b/openvpn/aws/awscreds.hpp index a0311ad91..fff6afbf7 100644 --- a/openvpn/aws/awscreds.hpp +++ b/openvpn/aws/awscreds.hpp @@ -40,6 +40,14 @@ namespace openvpn { { } + // can be used to load from HTTP creds + template + Creds(const CREDS& creds) + : access_key(creds.username), + secret_key(creds.password) + { + } + bool defined() const { return !access_key.empty() && !secret_key.empty(); From ae4e358efd95326b66f3f565cbb1be7eee56965d Mon Sep 17 00:00:00 2001 From: James Yonan Date: Fri, 13 May 2022 10:35:16 -0600 Subject: [PATCH 040/346] write_binary_unix(): added WRITE_BINARY_UNIX_EXISTING flag Signed-off-by: James Yonan --- openvpn/common/fileunix.hpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openvpn/common/fileunix.hpp b/openvpn/common/fileunix.hpp index 6a0fb8266..62749183b 100644 --- a/openvpn/common/fileunix.hpp +++ b/openvpn/common/fileunix.hpp @@ -50,6 +50,7 @@ namespace openvpn { OPENVPN_EXCEPTION(file_unix_error); // write binary buffer to file + static constexpr mode_t WRITE_BINARY_UNIX_EXISTING = 010000; // special mode that is useful for writing /proc files inline void write_binary_unix(const std::string& fn, const mode_t mode, const std::uint64_t mtime_ns, // set explicit modification-time in nanoseconds since epoch, or 0 to defer to system @@ -57,7 +58,10 @@ namespace openvpn { const ssize_t size) { // open - ScopedFD fd(::open(fn.c_str(), O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, mode)); + int flags = O_WRONLY|O_CLOEXEC; + if (!(mode & WRITE_BINARY_UNIX_EXISTING)) + flags |= O_CREAT|O_TRUNC; + ScopedFD fd(::open(fn.c_str(), flags, mode & (WRITE_BINARY_UNIX_EXISTING-1))); if (!fd.defined()) { const int eno = errno; From ed074447220bfbd319d0ee31d7022b0ff07e8eae Mon Sep 17 00:00:00 2001 From: James Yonan Date: Fri, 13 May 2022 11:01:28 -0600 Subject: [PATCH 041/346] OptionList::extend(): use range-based for loop and add rvalue reference variant Signed-off-by: James Yonan --- openvpn/common/options.hpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/openvpn/common/options.hpp b/openvpn/common/options.hpp index 010b31a0f..bec9033ee 100644 --- a/openvpn/common/options.hpp +++ b/openvpn/common/options.hpp @@ -981,12 +981,11 @@ namespace openvpn { // Append elements in other to self, // caller should call update_map() after this function. - void extend(const OptionList& other, FilterBase* filt) + void extend(const OptionList& other, FilterBase* filt=nullptr) { reserve(size() + other.size()); - for (std::vector