diff --git a/.travis.yml b/.travis.yml index f19a4db..d8551dc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,8 +9,8 @@ env: - secure: "dqiLqbzug/xs6F4Q9ei1pGpNf9Q6H3+iKN1W+P0TtODbCXPr/mLWdvHGVMIMqr7H7rBrIUPFPrfqd80nu3jQuQonjcHK/XyJJfmf5hUdhGAszSaixhWnGfVmn/VSV7/5+9DGAU3l9S6YZg4lvi12+cOrlblNgx8GeI5VdN/6HBSHkEqKNI56qn3Y+ugSdLeL1opmzlY58vRsCCmpBH8Ronn4tmSyi85/WZXfF43o9FGGJcygdh6QVWA1CDdNMeLTCt9ld+oToUIiFLiUrhfS1JpSvzysz2xsuEntxZaTMDYPyL4+O8Mj/scl6ejLLXzxTNa7AZOgySLBahf+F4b+yhL1deSVuu40MfxPW6XiM1jKy3KPH/GlYgM8CZQ3D1hQIq1CIUg8DgnTa06RUzevsR5DqDvz+EcPanFHE7dHGrPy9Rs/0y59dNHp3qWKjWMoSA06GerbF61XFOb4mcE29053kV8uxqIa5ZShZ/ndoLeVpQ4mZ+/XSkUybysVl0gWrKnnNNEPtqrdmKf+jlmKY0jyRPdwf425Ldn+wcbGw9ZEnkosYzqAhDBDX4OETAKLi8G0FEYECKKQcd1OX+HNvsOIyOAoLOj7H30F8UkPsjR3ysdIEmc6702ly06gDYjWmwQaCigL/1ktRKgf7ePB0HS+8fOa5SML7619kQrGrWA=" - PREFIX="${HOME}/opt" - ASIO_VERSION="862aed305dcf91387535519c9549c17630339a12" - - LZ4_VERSION="1.7.5" - - MBEDTLS_VERSION="2.5.1" + - LZ4_VERSION="1.8.3" + - MBEDTLS_VERSION="2.7.5" - MBEDTLS_CFLAGS="-I${PREFIX}/include" - MBEDTLS_LIBS="-lmbedtls -lmbedx509 -lmbedcrypto" - OPENSSL_VERSION="1.0.2l" @@ -22,11 +22,9 @@ matrix: include: - env: SSLLIB="openssl" os: osx - osx_image: xcode8.3 compiler: clang - env: SSLLIB="mbedtls" os: osx - osx_image: xcode8.3 compiler: clang - env: SSLLIB="openssl" RUN_COVERITY_SCAN="1" os: linux diff --git a/client/ovpncli.cpp b/client/ovpncli.cpp index 656e16d..97ffe2a 100644 --- a/client/ovpncli.cpp +++ b/client/ovpncli.cpp @@ -259,10 +259,10 @@ namespace openvpn { parent = parent_arg; } - virtual bool socket_protect(int socket) + bool socket_protect(int socket, IP::Addr endpoint) override { if (parent) - return parent->socket_protect(socket); + return parent->socket_protect(socket, endpoint.to_string(), endpoint.is_ipv6()); else return true; } diff --git a/client/ovpncli.hpp b/client/ovpncli.hpp index 5aa12bf..7132313 100644 --- a/client/ovpncli.hpp +++ b/client/ovpncli.hpp @@ -466,7 +466,8 @@ namespace openvpn { // Callback to "protect" a socket from being routed through the tunnel. // Will be called from the thread executing connect(). - virtual bool socket_protect(int socket) = 0; + // The remote and ipv6 are the remote host this socket will connect to + virtual bool socket_protect(int socket, std::string remote, bool ipv6) = 0; // Primary VPN client connect method, doesn't return until disconnect. // Should be called by a worker thread. This method will make callbacks diff --git a/deps/lib-versions b/deps/lib-versions index 63a16eb..90a5c26 100644 --- a/deps/lib-versions +++ b/deps/lib-versions @@ -1,8 +1,8 @@ export ASIO_VERSION=asio-1-12-0 export ASIO_CSUM=fa8c3a16dc2163f5b3451f2a14ce95277c971f46700497d4e94af6059c00dc06 -export LZ4_VERSION=lz4-1.8.0 -export LZ4_CSUM=2ca482ea7a9bb103603108b5a7510b7592b90158c151ff50a28f1ca8389fccf6 +export LZ4_VERSION=lz4-1.8.3 +export LZ4_CSUM=33af5936ac06536805f9745e0b6d61da606a1f8b4cc5c04dd3cbaca3b9b4fc43 export MBEDTLS_VERSION=mbedtls-2.7.5 export MBEDTLS_CSUM=a1302ad9094aabb9880d2755927b466a6bac8e02b68e04dee77321f3859e9b40 diff --git a/javacli/build-android b/javacli/build-android index 72bfcf1..2fcfa40 100755 --- a/javacli/build-android +++ b/javacli/build-android @@ -76,6 +76,7 @@ $GPP_CMD \ -DASIO_NO_DEPRECATED \ -DHAVE_LZ4 \ -DOPENVPN_USE_TLS_MD5 \ + -DASIO_HAS_STD_STRING_VIEW \ -I$O3/core/client \ -I$O3/core \ $common \ diff --git a/openvpn/buffer/buffer.hpp b/openvpn/buffer/buffer.hpp index 986d1cb..30c35b3 100644 --- a/openvpn/buffer/buffer.hpp +++ b/openvpn/buffer/buffer.hpp @@ -557,7 +557,8 @@ namespace openvpn { init_headroom(headroom); } - void append(const BufferType& other) + template + void append(const B& other) { write(other.c_data(), other.size()); } diff --git a/openvpn/client/cliopt.hpp b/openvpn/client/cliopt.hpp index 419fa6b..933bcc8 100644 --- a/openvpn/client/cliopt.hpp +++ b/openvpn/client/cliopt.hpp @@ -693,6 +693,7 @@ namespace openvpn { cp->dc_deferred = true; // defer data channel setup until after options pull cp->tls_auth_factory.reset(new CryptoOvpnHMACFactory()); cp->tls_crypt_factory.reset(new CryptoTLSCryptFactory()); + cp->tls_crypt_metadata_factory.reset(new CryptoTLSCryptMetadataFactory()); cp->tlsprf_factory.reset(new CryptoTLSPRFFactory()); cp->ssl_factory = cc->new_factory(); cp->load(opt, *proto_context_options, config.default_key_direction, false); diff --git a/openvpn/client/cliproto.hpp b/openvpn/client/cliproto.hpp index ba3a942..db5072d 100644 --- a/openvpn/client/cliproto.hpp +++ b/openvpn/client/cliproto.hpp @@ -600,6 +600,13 @@ namespace openvpn { else OPENVPN_LOG("Options continuation..."); } + else if (received_options.complete() && string::starts_with(msg, "PUSH_REPLY,")) + { + // We got a PUSH REPLY in the middle of a session. Ignore it apart from + // updating the auth-token if included in the push reply + auto opts = OptionList::parse_from_csv_static(msg.substr(11), nullptr); + extract_auth_token(opts); + } else if (string::starts_with(msg, "AUTH_FAILED")) { std::string reason; diff --git a/openvpn/client/remotelist.hpp b/openvpn/client/remotelist.hpp index 458a90a..34d80a8 100644 --- a/openvpn/client/remotelist.hpp +++ b/openvpn/client/remotelist.hpp @@ -276,8 +276,7 @@ namespace openvpn { PreResolve(openvpn_io::io_context& io_context_arg, const RemoteList::Ptr& remote_list_arg, const SessionStats::Ptr& stats_arg) - : io_context(io_context_arg), - resolver(io_context_arg), + : resolver(io_context_arg), notify_callback(nullptr), remote_list(remote_list_arg), stats(stats_arg), @@ -385,7 +384,6 @@ namespace openvpn { } } - openvpn_io::io_context& io_context; openvpn_io::ip::tcp::resolver resolver; NotifyCallback* notify_callback; RemoteList::Ptr remote_list; diff --git a/openvpn/common/bigmutex.hpp b/openvpn/common/bigmutex.hpp index 134f65b..bf3cf7d 100644 --- a/openvpn/common/bigmutex.hpp +++ b/openvpn/common/bigmutex.hpp @@ -34,11 +34,13 @@ #include +#include + namespace openvpn { namespace bigmutex { OPENVPN_EXTERN std::recursive_mutex the_recursive_mutex; } - + #ifdef OPENVPN_ENABLE_BIGMUTEX #define OPENVPN_ASYNC_HANDLER \ std::lock_guard lg(bigmutex::the_recursive_mutex); diff --git a/openvpn/common/endian.hpp b/openvpn/common/endian.hpp index dd6ba70..53dd015 100644 --- a/openvpn/common/endian.hpp +++ b/openvpn/common/endian.hpp @@ -19,25 +19,13 @@ // along with this program in the COPYING file. // If not, see . -#ifndef OPENVPN_COMMON_ENDIAN_H -#define OPENVPN_COMMON_ENDIAN_H +#pragma once -#include - -// test for machine endiannes -#if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && defined(__ORDER_LITTLE_ENDIAN__) -#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -#define OPENVPN_BIG_ENDIAN -#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ -#define OPENVPN_LITTLE_ENDIAN -#endif -#elif defined(_WIN32) -#define OPENVPN_LITTLE_ENDIAN // assume that Windows is always little-endian -#endif +#include namespace openvpn { namespace Endian { -# ifdef OPENVPN_LITTLE_ENDIAN +# if defined(OPENVPN_LITTLE_ENDIAN) inline size_t e16(const size_t v) { return v; @@ -62,7 +50,7 @@ namespace openvpn { { return 1-v; } -# elif OPENVPN_BIG_ENDIAN +# elif defined(OPENVPN_BIG_ENDIAN) inline size_t e16rev(const size_t v) { return v; @@ -92,5 +80,3 @@ namespace openvpn { # endif } } // namespace openvpn - -#endif // OPENVPN_COMMON_ENDIAN_H diff --git a/openvpn/common/endian_platform.hpp b/openvpn/common/endian_platform.hpp new file mode 100644 index 0000000..353a4e8 --- /dev/null +++ b/openvpn/common/endian_platform.hpp @@ -0,0 +1,35 @@ +// OpenVPN -- An application to securely tunnel IP networks +// over a single port, with support for SSL/TLS-based +// session authentication and key exchange, +// packet encryption, packet authentication, and +// packet compression. +// +// Copyright (C) 2012-2018 OpenVPN Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License Version 3 +// 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program in the COPYING file. +// If not, see . + +#pragma once + +#include + +// test for machine endiannes +#if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && defined(__ORDER_LITTLE_ENDIAN__) +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#define OPENVPN_BIG_ENDIAN +#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#define OPENVPN_LITTLE_ENDIAN +#endif +#elif defined(_WIN32) +#define OPENVPN_LITTLE_ENDIAN // assume that Windows is always little-endian +#endif diff --git a/openvpn/common/ffs.hpp b/openvpn/common/ffs.hpp index da8373d..b92b942 100644 --- a/openvpn/common/ffs.hpp +++ b/openvpn/common/ffs.hpp @@ -32,18 +32,88 @@ namespace openvpn { #if defined(__GNUC__) - inline int find_first_set(unsigned int v) + template + inline constexpr int n_bits_type() + { + return sizeof(T) * 8; + } + + template + inline constexpr int n_bits_type(const T& v) + { + return sizeof(v) * 8; + } + + inline int find_first_set(const unsigned int v) { if (!v) return 0; return __builtin_ffs(v); } - inline int find_last_set(unsigned int v) + inline int find_first_set(const int v) + { + return find_first_set(static_cast(v)); + } + + inline int find_last_set(const unsigned int v) { if (!v) return 0; - return 32 - __builtin_clz(v); + return n_bits_type(v) - __builtin_clz(v); + } + + inline int find_last_set(const int v) + { + return find_last_set(static_cast(v)); + } + + inline int find_first_set(const unsigned long v) + { + if (!v) + return 0; + return __builtin_ffsl(v); + } + + inline int find_first_set(const long v) + { + return find_first_set(static_cast(v)); + } + + inline int find_last_set(const unsigned long v) + { + if (!v) + return 0; + return n_bits_type(v) - __builtin_clzl(v); + } + + inline int find_last_set(const long v) + { + return find_last_set(static_cast(v)); + } + + inline int find_first_set(const unsigned long long v) + { + if (!v) + return 0; + return __builtin_ffsll(v); + } + + inline int find_first_set(const long long v) + { + return find_first_set(static_cast(v)); + } + + inline int find_last_set(const unsigned long long v) + { + if (!v) + return 0; + return n_bits_type(v) - __builtin_clzll(v); + } + + inline int find_last_set(const long long v) + { + return find_last_set(static_cast(v)); } #elif defined(_MSC_VER) diff --git a/openvpn/common/fileatomic.hpp b/openvpn/common/fileatomic.hpp index 2d22ec6..009bc35 100644 --- a/openvpn/common/fileatomic.hpp +++ b/openvpn/common/fileatomic.hpp @@ -30,7 +30,8 @@ #error atomic file methods not supported on Windows #endif -#include // for rename() +#include // for rename() +#include // for unlink() #include #include @@ -63,6 +64,7 @@ namespace openvpn { if (::rename(tfn.c_str(), fn.c_str()) == -1) { const int eno = errno; + ::unlink(tfn.c_str()); // move failed, so delete the temporary file OPENVPN_THROW(file_unix_error, "error moving '" << tfn << "' -> '" << fn << "' : " << strerror_str(eno)); } } diff --git a/openvpn/common/jsonlib.hpp b/openvpn/common/jsonlib.hpp index a8eba12..193c9a5 100644 --- a/openvpn/common/jsonlib.hpp +++ b/openvpn/common/jsonlib.hpp @@ -26,5 +26,6 @@ #include "json/json.h" // JsonCpp library #elif defined(HAVE_OPENVPN_COMMON) #define HAVE_JSON +#define OPENVPN_JSON_INTERNAL #include // internal OpenVPN JSON implementation #endif diff --git a/openvpn/common/modstat.hpp b/openvpn/common/modstat.hpp index 609c449..f89a857 100644 --- a/openvpn/common/modstat.hpp +++ b/openvpn/common/modstat.hpp @@ -28,8 +28,12 @@ #include +#include + namespace openvpn { +#if defined(OPENVPN_PLATFORM_LINUX) + inline int update_file_mod_time_nanoseconds(const std::string& filename, const std::uint64_t nanoseconds_since_epooch) { @@ -54,4 +58,20 @@ namespace openvpn { return 0; } +#else + + inline int update_file_mod_time_nanoseconds(const std::string& filename, + const std::uint64_t nanoseconds_since_epooch) + { + return 0; + } + + inline int update_file_mod_time_nanoseconds(const int fd, + const std::uint64_t nanoseconds_since_epooch) + { + return 0; + } + +#endif + } diff --git a/openvpn/common/path.hpp b/openvpn/common/path.hpp index e57c651..4d9d017 100644 --- a/openvpn/common/path.hpp +++ b/openvpn/common/path.hpp @@ -113,6 +113,56 @@ namespace openvpn { return ""; } + // return true if path is a regular file that doesn't try to traverse via ".." or "/..." + inline bool is_contained(const std::string& path) + { + if (path.empty()) + return false; + if (win_dev(path, false)) + return false; + if (is_dirsep(path[0])) + return false; + + // look for ".." in path + enum State { + SEP, // immediately after separator + MID, // middle of dir + DOT_2, // looking for second '.' + POST_DOT_2, // after ".." + }; + State state = SEP; + for (const auto c : path) + { + switch (state) + { + case SEP: + if (c == '.') + state = DOT_2; + else if (!is_dirsep(c)) + state = MID; + break; + case MID: + if (is_dirsep(c)) + state = SEP; + break; + case DOT_2: + if (c == '.') + state = POST_DOT_2; + else if (is_dirsep(c)) + state = SEP; + else + state = MID; + break; + case POST_DOT_2: + if (is_dirsep(c)) + return false; + state = MID; + break; + } + } + return state != POST_DOT_2; + } + inline std::string ext(const std::string& basename) { const size_t pos = basename.find_last_of('.'); diff --git a/openvpn/common/runcontext.hpp b/openvpn/common/runcontext.hpp index 1789427..5aa7131 100644 --- a/openvpn/common/runcontext.hpp +++ b/openvpn/common/runcontext.hpp @@ -325,7 +325,8 @@ namespace openvpn { cancel(); } - void signal(const openvpn_io::error_code& error, int signum) + protected: + virtual void signal(const openvpn_io::error_code& error, int signum) { if (!error && !halt) { @@ -346,10 +347,14 @@ namespace openvpn { signal_rearm(); break; #endif + default: + signal_rearm(); + break; } } } + private: void signal_rearm() { signals->register_signals_all([self=Ptr(this)](const openvpn_io::error_code& error, int signal_number) @@ -392,7 +397,6 @@ namespace openvpn { // servlist and related vars protected by mutex std::vector servlist; int thread_count = 0; - volatile bool halt = false; // stop Stop* async_stop_ = nullptr; @@ -404,6 +408,9 @@ namespace openvpn { // logging Log::Context log_context; Log::Context::Wrapper log_wrap; // must be constructed after log_context + + protected: + volatile bool halt = false; }; } diff --git a/openvpn/common/string.hpp b/openvpn/common/string.hpp index 8807693..823cd77 100644 --- a/openvpn/common/string.hpp +++ b/openvpn/common/string.hpp @@ -296,7 +296,7 @@ namespace openvpn { inline bool contains_non_space_ctrl(const std::string& str) { for (auto &c : str) - if (!is_space(c) && is_ctrl(c)) + if ((!is_space(c) && is_ctrl(c)) || c == 127) return true; return false; } diff --git a/openvpn/common/waitbarrier.hpp b/openvpn/common/waitbarrier.hpp index 604f38e..182f977 100644 --- a/openvpn/common/waitbarrier.hpp +++ b/openvpn/common/waitbarrier.hpp @@ -26,9 +26,16 @@ #include namespace openvpn { + +#ifdef HAVE_VALGRIND + static constexpr unsigned int WAIT_BARRIER_TIMEOUT = 300; +#else + static constexpr unsigned int WAIT_BARRIER_TIMEOUT = 30; +#endif + template inline void event_loop_wait_barrier(THREAD_COMMON& tc, - const unsigned int seconds=30) + const unsigned int seconds=WAIT_BARRIER_TIMEOUT) { // barrier prior to event-loop entry switch (tc.event_loop_bar.wait(seconds)) diff --git a/openvpn/crypto/tls_crypt.hpp b/openvpn/crypto/tls_crypt.hpp index d91e8b8..f95ab00 100644 --- a/openvpn/crypto/tls_crypt.hpp +++ b/openvpn/crypto/tls_crypt.hpp @@ -89,11 +89,8 @@ namespace openvpn { bool hmac_gen(unsigned char *header, const size_t header_len, const unsigned char *payload, const size_t payload_len) { - if (header_len < head_size + output_hmac_size()) - return false; - - hmac_pre(header, payload, payload_len); - ctx_hmac.final(header + head_size); + hmac_pre(header, header_len, payload, payload_len); + ctx_hmac.final(header + header_len); return true; } @@ -103,13 +100,10 @@ namespace openvpn { { unsigned char local_hmac[CRYPTO_API::HMACContext::MAX_HMAC_SIZE]; - if (header_len < head_size + output_hmac_size()) - return false; - - hmac_pre(header, payload, payload_len); + hmac_pre(header, header_len, payload, payload_len); ctx_hmac.final(local_hmac); - return !crypto::memneq(header + head_size, local_hmac, output_hmac_size()); + return !crypto::memneq(header + header_len, local_hmac, output_hmac_size()); } size_t encrypt(const unsigned char *iv, unsigned char *out, const size_t olen, @@ -132,11 +126,11 @@ namespace openvpn { private: // assume length check on header has already been performed - void hmac_pre(const unsigned char *header, const unsigned char *payload, - const size_t payload_len) + void hmac_pre(const unsigned char *header, const size_t header_len, + const unsigned char *payload, const size_t payload_len) { ctx_hmac.reset(); - ctx_hmac.update(header, head_size); + ctx_hmac.update(header, header_len); ctx_hmac.update(payload, payload_len); } @@ -159,19 +153,8 @@ namespace openvpn { typename CRYPTO_API::HMACContext ctx_hmac; typename CRYPTO_API::CipherContext ctx_crypt; int mode; - - static const size_t head_size; }; - // initialize static member with non-constexpr. - // This is the size of the header in a TLSCrypt-wrapped packets, - // excluding the HMAC. Format: - // - // [OP] [PSID] [PID] [HMAC] [...] - // - template - const size_t TLSCrypt::head_size = 1 + ProtoSessionID::SIZE + PacketID::size(PacketID::LONG_FORM); - // OvpnHMAC wrapper API using dynamic polymorphism class TLSCryptInstance : public RC @@ -208,8 +191,19 @@ namespace openvpn { virtual TLSCryptInstance::Ptr new_obj_send() = 0; virtual TLSCryptInstance::Ptr new_obj_recv() = 0; + + static const size_t hmac_offset; }; + // initialize static member with non-constexpr. + // This is the size of the header in a TLSCrypt-wrapped packets, + // excluding the HMAC. Format: + // + // [OP] [PSID] [PID] [HMAC] [...] + // + const size_t TLSCryptContext::hmac_offset = 1 + ProtoSessionID::SIZE + + PacketID::size(PacketID::LONG_FORM); + class TLSCryptFactory : public RC { public: @@ -244,21 +238,6 @@ namespace openvpn { return tls_crypt.output_hmac_size(); } - void ovpn_hmac_reset() - { - tls_crypt.ovpn_hmac_reset(); - } - - void ovpn_hmac_update(const unsigned char *in, const size_t in_size) - { - tls_crypt.ovpn_hmac_update(in, in_size); - } - - void ovpn_hmac_write(unsigned char *out) - { - tls_crypt.ovpn_hmac_write(out); - } - bool hmac_gen(unsigned char *header, const size_t header_len, const unsigned char *payload, const size_t payload_len) { diff --git a/openvpn/crypto/tls_crypt_v2.hpp b/openvpn/crypto/tls_crypt_v2.hpp new file mode 100644 index 0000000..41d5bb1 --- /dev/null +++ b/openvpn/crypto/tls_crypt_v2.hpp @@ -0,0 +1,198 @@ +// OpenVPN -- An application to securely tunnel IP networks +// over a single port, with support for SSL/TLS-based +// session authentication and key exchange, +// packet encryption, packet authentication, and +// packet compression. +// +// Copyright (C) 2017-2018 OpenVPN Technologies, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License Version 3 +// 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 in the COPYING file. +// If not, see . + +// Classes for handling OpenVPN tls-crypt-v2 internals + +#ifndef OPENVPN_CRYPTO_TLS_CRYPT_V2_H +#define OPENVPN_CRYPTO_TLS_CRYPT_V2_H + +#include + +#include +#include +#include +#include +#include + +namespace openvpn { + class TLSCryptV2ServerKey + { + public: + OPENVPN_SIMPLE_EXCEPTION(tls_crypt_v2_server_key_parse_error); + OPENVPN_SIMPLE_EXCEPTION(tls_crypt_v2_server_key_encode_error); + OPENVPN_SIMPLE_EXCEPTION(tls_crypt_v2_server_key_bad_size); + + TLSCryptV2ServerKey() + : key_size(128), + key(key_size, BufferAllocated::DESTRUCT_ZERO) + {} + + bool defined() const + { + return key.defined(); + } + + void parse(const std::string& key_text) + { + if (!SSLLib::PEMAPI::pem_decode(key, key_text.c_str(), key_text.length(), + tls_crypt_v2_server_key_name)) + throw tls_crypt_v2_server_key_parse_error(); + + if (key.size() != key_size) + throw tls_crypt_v2_server_key_bad_size(); + } + + void extract_key(OpenVPNStaticKey& tls_key) + { + std::memcpy(tls_key.raw_alloc(), key.c_data(), key_size); + } + + std::string render() const + { + BufferAllocated data(32 + 2 * key.size(), 0); + + if (!SSLLib::PEMAPI::pem_encode(data, key.c_data(), key.size(), + tls_crypt_v2_server_key_name)) + throw tls_crypt_v2_server_key_encode_error(); + + return std::string((const char *)data.c_data()); + } + + private: + const size_t key_size; + BufferAllocated key; + static const std::string tls_crypt_v2_server_key_name; + }; + + const std::string TLSCryptV2ServerKey::tls_crypt_v2_server_key_name = "OpenVPN tls-crypt-v2 server key"; + + class TLSCryptV2ClientKey + { + public: + enum { + WKC_MAX_SIZE = 1024, // bytes + }; + + OPENVPN_SIMPLE_EXCEPTION(tls_crypt_v2_client_key_parse_error); + OPENVPN_SIMPLE_EXCEPTION(tls_crypt_v2_client_key_encode_error); + OPENVPN_SIMPLE_EXCEPTION(tls_crypt_v2_client_key_bad_size); + + TLSCryptV2ClientKey() = delete; + + TLSCryptV2ClientKey(TLSCryptContext::Ptr context) + : key_size(OpenVPNStaticKey::KEY_SIZE), + tag_size(context->digest_size()) + {} + + bool defined() const + { + return key.defined() && wkc.defined(); + } + + void parse(const std::string& key_text) + { + BufferAllocated data(key_size + WKC_MAX_SIZE, BufferAllocated::DESTRUCT_ZERO); + + if (!SSLLib::PEMAPI::pem_decode(data, key_text.c_str(), key_text.length(), + tls_crypt_v2_client_key_name)) + throw tls_crypt_v2_client_key_parse_error(); + + if (data.size() < (tag_size + key_size)) + throw tls_crypt_v2_client_key_bad_size(); + + key.init(data.data(), key_size, BufferAllocated::DESTRUCT_ZERO); + wkc.init(data.data() + key_size, data.size() - key_size, BufferAllocated::DESTRUCT_ZERO); + } + + void extract_key(OpenVPNStaticKey& tls_key) + { + std::memcpy(tls_key.raw_alloc(), key.c_data(), key_size); + } + + std::string render() const + { + BufferAllocated data(32 + 2 * (key.size() + wkc.size()), 0); + BufferAllocated in(key, BufferAllocated::GROW); + in.append(wkc); + + if (!SSLLib::PEMAPI::pem_encode(data, in.c_data(), in.size(), tls_crypt_v2_client_key_name)) + throw tls_crypt_v2_client_key_encode_error(); + + return std::string((const char *)data.c_data()); + } + + void extract_wkc(BufferAllocated& wkc_out) const + { + wkc_out = wkc; + } + + private: + BufferAllocated key; + BufferAllocated wkc; + + const size_t key_size; + const size_t tag_size; + + static const std::string tls_crypt_v2_client_key_name; + }; + + const std::string TLSCryptV2ClientKey::tls_crypt_v2_client_key_name = "OpenVPN tls-crypt-v2 client key"; + + // the user can extend the TLSCryptMetadata and the TLSCryptMetadataFactory + // classes to implement its own metadata verification method. + // + // default method is to *ignore* the metadata contained in the WKc sent by the client + class TLSCryptMetadata : public RC + { + public: + typedef RCPtr Ptr; + + // override this method with your own verification mechanism. + // + // If type is -1 it means that metadata is empty. + // + virtual bool verify(int type, Buffer& metadata) const + { + return true; + } + }; + + // abstract class to be extended when creating other factories + class TLSCryptMetadataFactory : public RC + { + public: + typedef RCPtr Ptr; + + virtual TLSCryptMetadata::Ptr new_obj() = 0; + }; + + // factory implementation for the basic verification method + class CryptoTLSCryptMetadataFactory : public TLSCryptMetadataFactory + { + public: + TLSCryptMetadata::Ptr new_obj() + { + return new TLSCryptMetadata(); + } + }; +} + +#endif /* OPENVPN_CRYPTO_TLS_CRYPT_V2_H */ diff --git a/openvpn/error/error.hpp b/openvpn/error/error.hpp index 740dba8..9ebd9c1 100644 --- a/openvpn/error/error.hpp +++ b/openvpn/error/error.hpp @@ -70,6 +70,7 @@ namespace openvpn { PRIMARY_EXPIRE, // primary key context expired TLS_VERSION_MIN, // peer cannot handshake at our minimum required TLS version TLS_AUTH_FAIL, // tls-auth HMAC verification failed + TLS_CRYPT_META_FAIL, // tls-crypt-v2 metadata verification failed CERT_VERIFY_FAIL, // peer certificate verification failure PEM_PASSWORD_FAIL, // incorrect or missing PEM private key decryption password AUTH_FAILED, // general authentication failure @@ -145,6 +146,7 @@ namespace openvpn { "PRIMARY_EXPIRE", "TLS_VERSION_MIN", "TLS_AUTH_FAIL", + "TLS_CRYPT_META_FAIL", "CERT_VERIFY_FAIL", "PEM_PASSWORD_FAIL", "AUTH_FAILED", diff --git a/openvpn/http/reply.hpp b/openvpn/http/reply.hpp index 6f3c058..9c303af 100644 --- a/openvpn/http/reply.hpp +++ b/openvpn/http/reply.hpp @@ -93,7 +93,6 @@ namespace openvpn { public: enum status { - undefined, pending, fail, success, diff --git a/openvpn/init/initprocess.hpp b/openvpn/init/initprocess.hpp index 8e18fc2..05c5b2a 100644 --- a/openvpn/init/initprocess.hpp +++ b/openvpn/init/initprocess.hpp @@ -59,10 +59,6 @@ namespace openvpn { { base64_uninit_static(); } - - private: - // initialize SSL library - crypto_init crypto_init_; }; // process-wide singular instance diff --git a/openvpn/ip/ipcommon.hpp b/openvpn/ip/ipcommon.hpp index 34afe6c..5e52e30 100644 --- a/openvpn/ip/ipcommon.hpp +++ b/openvpn/ip/ipcommon.hpp @@ -36,6 +36,11 @@ namespace openvpn { UDP = 17, /* UDP protocol */ }; + enum { + IPv4 = 4, + IPv6 = 6 + }; + inline unsigned int version(const std::uint8_t version_len_prio) { return (version_len_prio >> 4) & 0x0F; diff --git a/openvpn/ip/tcp.hpp b/openvpn/ip/tcp.hpp new file mode 100644 index 0000000..04cf3fd --- /dev/null +++ b/openvpn/ip/tcp.hpp @@ -0,0 +1,90 @@ +// OpenVPN -- An application to securely tunnel IP networks +// over a single port, with support for SSL/TLS-based +// session authentication and key exchange, +// packet encryption, packet authentication, and +// packet compression. +// +// Copyright (C) 2012-2017 OpenVPN Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License Version 3 +// 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program in the COPYING file. +// If not, see . + +#pragma once + +#include +#include + +namespace openvpn { + +#pragma pack(push) +#pragma pack(1) + + struct TCPHeader { + static unsigned int length(const std::uint8_t doff_res) + { + return ((doff_res) & 0xF0) >> 2; + } + + std::uint16_t source; + std::uint16_t dest; + std::uint32_t seq; + std::uint32_t ack_seq; + std::uint8_t doff_res; + std::uint8_t flags; + std::uint16_t window; + std::uint16_t check; + std::uint16_t urgent_p; + + // helper enum to parse options in TCP header + enum { + OPT_EOL = 0, + OPT_NOP = 1, + OPT_MAXSEG = 2, + OPTLEN_MAXSEG = 4 + }; + + enum { + FLAG_SYN = 1 << 1 + }; + }; + +#pragma pack(pop) + + /* + * The following routine is used to update an + * internet checksum. "acc" is a 32-bit + * accumulation of all the changes to the + * checksum (adding in old 16-bit words and + * subtracting out new words), and "cksum" + * is the checksum value to be updated. + */ + inline void tcp_adjust_checksum(int acc, std::uint16_t& cksum) + { + int _acc = acc; + _acc += cksum; + if (_acc < 0) + { + _acc = -_acc; + _acc = (_acc >> 16) + (_acc & 0xffff); + _acc += _acc >> 16; + cksum = (uint16_t)~_acc; + } + else + { + _acc = (_acc >> 16) + (_acc & 0xffff); + _acc += _acc >> 16; + cksum = (uint16_t)_acc; + } + } +} + diff --git a/openvpn/mbedtls/ssl/sslctx.hpp b/openvpn/mbedtls/ssl/sslctx.hpp index d043770..6b277c5 100644 --- a/openvpn/mbedtls/ssl/sslctx.hpp +++ b/openvpn/mbedtls/ssl/sslctx.hpp @@ -826,8 +826,15 @@ namespace openvpn { // set our own certificate, supporting chain (i.e. extra-certs), and external private key if (c.crt_chain) { - epki_ctx.epki_enable(ctx, epki_decrypt, epki_sign, epki_key_len); - mbedtls_ssl_conf_own_cert(sslconf, c.crt_chain->get(), epki_ctx.get()); + if (mbedtls_pk_get_type(&c.crt_chain.get()->get()->pk) == MBEDTLS_PK_RSA) + { + epki_ctx.epki_enable(ctx, epki_decrypt, epki_sign, epki_key_len); + mbedtls_ssl_conf_own_cert(sslconf, c.crt_chain->get(), epki_ctx.get()); + } + else + { + throw MbedTLSException("cert has unsupported type for external pki support"); + } } else throw MbedTLSException("cert is undefined"); @@ -1217,7 +1224,11 @@ namespace openvpn { { const int SHA_DIGEST_LEN = 20; static_assert(sizeof(AuthCert::issuer_fp) == SHA_DIGEST_LEN, "size inconsistency"); - mbedtls_sha1(cert->raw.p, cert->raw.len, ssl->authcert->issuer_fp); + if(mbedtls_sha1_ret(cert->raw.p, cert->raw.len, ssl->authcert->issuer_fp)) + { + OPENVPN_LOG_SSL("VERIFY FAIL -- SHA1 calculation failed."); + fail = true; + } } } else if (depth == 0) // leaf-cert diff --git a/openvpn/mbedtls/util/pem.hpp b/openvpn/mbedtls/util/pem.hpp new file mode 100644 index 0000000..e0524e6 --- /dev/null +++ b/openvpn/mbedtls/util/pem.hpp @@ -0,0 +1,77 @@ +// OpenVPN -- An application to securely tunnel IP networks +// over a single port, with support for SSL/TLS-based +// session authentication and key exchange, +// packet encryption, packet authentication, and +// packet compression. +// +// Copyright (C) 2017-2018 OpenVPN Technologies, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License Version 3 +// 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 in the COPYING file. +// If not, see . + +// Wrap the mbedTLS PEM API defined in so +// that it can be used as part of the crypto layer of the OpenVPN core. + +#ifndef OPENVPN_MBEDTLS_UTIL_PEM_H +#define OPENVPN_MBEDTLS_UTIL_PEM_H + +#include + +namespace openvpn { + class MbedTLSPEM + { + public: + static bool pem_encode(BufferAllocated& dst, const unsigned char *src, + size_t src_len, const std::string& key_name) + { + std::string header = "-----BEGIN " + key_name + "-----\n"; + std::string footer = "-----END " + key_name + "-----\n"; + size_t out_len = 0; + + int ret = mbedtls_pem_write_buffer(header.c_str(), footer.c_str(), + src, src_len, dst.data(), + dst.max_size(), &out_len); + if (ret == 0) + dst.set_size(out_len); + else + { + char buf[128]; + mbedtls_strerror(ret, buf, 128); + OPENVPN_LOG("mbedtls_pem_write_buffer error: " << buf); + } + + return (ret == 0); + } + + static bool pem_decode(BufferAllocated& dst, const char *src, + size_t src_len, const std::string& key_name) + { + std::string header = "-----BEGIN " + key_name + "-----"; + std::string footer = "-----END " + key_name + "-----"; + mbedtls_pem_context ctx = { 0 }; + size_t out_len = 0; + + int ret = mbedtls_pem_read_buffer(&ctx, header.c_str(), footer.c_str(), + (unsigned char *)src, nullptr, 0, + &out_len); + if (ret == 0) + dst.init(ctx.buf, ctx.buflen, BufferAllocated::DESTRUCT_ZERO); + + mbedtls_pem_free(&ctx); + + return (ret == 0); + } + }; +}; + +#endif /* OPENVPN_MBEDTLS_UTIL_PEM_H */ diff --git a/openvpn/netconf/linux/gwnetlink.hpp b/openvpn/netconf/linux/gwnetlink.hpp new file mode 100644 index 0000000..7af5f30 --- /dev/null +++ b/openvpn/netconf/linux/gwnetlink.hpp @@ -0,0 +1,141 @@ +// OpenVPN -- An application to securely tunnel IP networks +// over a single port, with support for SSL/TLS-based +// session authentication and key exchange, +// packet encryption, packet authentication, and +// packet compression. +// +// Copyright (C) 2012-2017 OpenVPN Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License Version 3 +// 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program in the COPYING file. +// If not, see . + +// Find default gateways on Linux using ip route command + +#pragma once + +#include + +#include +#include +#include +#include +#include + +namespace openvpn { + class LinuxGWNetlink + { + public: + OPENVPN_EXCEPTION(linux_gw_netlink_error); + + LinuxGWNetlink(bool ipv6) + { + try + { + if (ipv6) + { + IPv6::Addr addr6; + + if (TunNetlink::SITNL::net_route_best_gw(IP::Route6(IPv6::Addr::from_zero(), 0), + addr6, dev_) < 0) + { + OPENVPN_THROW(linux_gw_netlink_error, + "error retrieving default IPv6 GW"); + } + + addr_ = IP::Addr::from_ipv6(addr6); + } + else + { + IPv4::Addr addr4; + + if (TunNetlink::SITNL::net_route_best_gw(IP::Route4(IPv4::Addr::from_zero(), 0), + addr4, dev_) < 0) + { + OPENVPN_THROW(linux_gw_netlink_error, + "error retrieving default IPv4 GW"); + } + + addr_ = IP::Addr::from_ipv4(addr4); + } + } catch (...) + { + /* nothing to do. just leave default GW unassigned */ + } + } + + const std::string& dev() const + { + return dev_; + } + + const IP::Addr& addr() const + { + return addr_; + } + + bool defined() const + { + return !dev_.empty() && addr_.defined(); + } + + std::string to_string() const + { + return dev_ + '/' + addr_.to_string(); + } + + private: + IP::Addr addr_; + std::string dev_; + }; + + struct LinuxGW46Netlink + { + LinuxGW46Netlink() + : v4(false), + v6(true) + { + } + + std::string to_string() const + { + std::string ret = "["; + if (v4.defined()) + { + ret += "4:"; + ret += v4.to_string(); + } + if (v6.defined()) + { + if (v4.defined()) + ret += ' '; + ret += "6:"; + ret += v6.to_string(); + } + ret += "]"; + return ret; + } + + std::string dev() const + { + if (v4.defined()) + return v4.dev(); + else if (v6.defined()) + return v6.dev(); + else + throw LinuxGWNetlink::linux_gw_netlink_error("cannot determine gateway interface"); + } + + LinuxGWNetlink v4; + LinuxGWNetlink v6; + }; +} diff --git a/openvpn/openssl/ssl/sslctx.hpp b/openvpn/openssl/ssl/sslctx.hpp index 540482f..678f331 100644 --- a/openvpn/openssl/ssl/sslctx.hpp +++ b/openvpn/openssl/ssl/sslctx.hpp @@ -520,6 +520,8 @@ namespace openvpn { static void init_static() { + SSL_library_init(); + mydata_index = SSL_get_ex_new_index(0, (char *)"OpenSSLContext::SSL", nullptr, nullptr, nullptr); // We actually override some of the OpenSSL SSLv23 methods here, @@ -867,7 +869,7 @@ namespace openvpn { try { // Create new SSL_CTX for server or client mode - const bool ssl23 = (config->force_aes_cbc_ciphersuites || (config->tls_version_min > TLSVersion::UNDEF)); + const bool ssl23 = (!config->force_aes_cbc_ciphersuites || (config->tls_version_min > TLSVersion::UNDEF)); if (config->mode.is_server()) { ctx = SSL_CTX_new(ssl23 ? SSL::ssl23_method_server() : TLSv1_server_method()); @@ -910,20 +912,17 @@ namespace openvpn { if (ssl23) { sslopt |= SSL_OP_NO_SSLv2; - if (!config->force_aes_cbc_ciphersuites || config->tls_version_min > TLSVersion::UNDEF) - { - sslopt |= SSL_OP_NO_SSLv3; - if (config->tls_version_min > TLSVersion::V1_0) - sslopt |= SSL_OP_NO_TLSv1; -# ifdef SSL_OP_NO_TLSv1_1 - if (config->tls_version_min > TLSVersion::V1_1) - sslopt |= SSL_OP_NO_TLSv1_1; -# endif -# ifdef SSL_OP_NO_TLSv1_2 - if (config->tls_version_min > TLSVersion::V1_2) - sslopt |= SSL_OP_NO_TLSv1_2; -# endif - } + sslopt |= SSL_OP_NO_SSLv3; + if (config->tls_version_min > TLSVersion::V1_0) + sslopt |= SSL_OP_NO_TLSv1; +# ifdef SSL_OP_NO_TLSv1_1 + if (config->tls_version_min > TLSVersion::V1_1) + sslopt |= SSL_OP_NO_TLSv1_1; +# endif +# ifdef SSL_OP_NO_TLSv1_2 + if (config->tls_version_min > TLSVersion::V1_2) + sslopt |= SSL_OP_NO_TLSv1_2; +# endif } SSL_CTX_set_options(ctx, sslopt); diff --git a/openvpn/openssl/util/pem.hpp b/openvpn/openssl/util/pem.hpp new file mode 100644 index 0000000..a3ea1cb --- /dev/null +++ b/openvpn/openssl/util/pem.hpp @@ -0,0 +1,103 @@ +// OpenVPN -- An application to securely tunnel IP networks +// over a single port, with support for SSL/TLS-based +// session authentication and key exchange, +// packet encryption, packet authentication, and +// packet compression. +// +// Copyright (C) 2017-2018 OpenVPN Technologies, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License Version 3 +// 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 in the COPYING file. +// If not, see . + +// Wrap the OpenSSL PEM API defined in so +// that it can be used as part of the crypto layer of the OpenVPN core. + +#ifndef OPENVPN_OPENSSL_UTIL_PEM_H +#define OPENVPN_OPENSSL_UTIL_PEM_H + +#include + +#include + +namespace openvpn { + class OpenSSLPEM + { + public: + static bool pem_encode(BufferAllocated& dst, const unsigned char *src, + size_t src_len, const std::string& key_name) + { + bool ret = false; + BIO *bio = BIO_new(BIO_s_mem()); + if (!bio) + return false; + + if (!PEM_write_bio(bio, key_name.c_str(), "", src, src_len)) + goto out; + + BUF_MEM *bptr; + BIO_get_mem_ptr(bio, &bptr); + dst.write((unsigned char *)bptr->data, bptr->length); + + ret = true; + +out: + if (!BIO_free(bio)) + ret = false; + + return ret; + } + + static bool pem_decode(BufferAllocated& dst, const char *src, + size_t src_len, const std::string& key_name) + { + bool ret = false; + BIO *bio; + + if (!(bio = BIO_new_mem_buf(src, src_len))) + throw OpenSSLException("Cannot open memory BIO for PEM decode"); + + char *name_read = NULL; + char *header_read = NULL; + uint8_t *data_read = NULL; + long data_read_len = 0; + if (!PEM_read_bio(bio, &name_read, &header_read, &data_read, + &data_read_len)) + { + OPENVPN_LOG("PEM decode failed"); + goto out; + } + + if (key_name.compare(std::string(name_read))) + { + OPENVPN_LOG("unexpected PEM name (got '" << name_read << + "', expected '" << key_name << "')"); + goto out; + } + + dst.write(data_read, data_read_len); + + ret = true; +out: + OPENSSL_free(name_read); + OPENSSL_free(header_read); + OPENSSL_free(data_read); + + if (!BIO_free(bio)) + ret = false; + + return ret; + } + }; +}; + +#endif /* OPENVPN_OPENSSL_UTIL_PEM_H */ diff --git a/openvpn/options/merge.hpp b/openvpn/options/merge.hpp index 4ee3173..1f59b6a 100644 --- a/openvpn/options/merge.hpp +++ b/openvpn/options/merge.hpp @@ -422,6 +422,8 @@ namespace openvpn { } if (d == "tls-crypt") return true; + if (d == "tls-crypt-v2") + return true; return false; } } diff --git a/openvpn/ssl/is_openvpn_protocol.hpp b/openvpn/ssl/is_openvpn_protocol.hpp index 57de8bf..8b9a436 100644 --- a/openvpn/ssl/is_openvpn_protocol.hpp +++ b/openvpn/ssl/is_openvpn_protocol.hpp @@ -34,6 +34,7 @@ namespace openvpn { inline bool is_openvpn_protocol(const unsigned char *p, const size_t len) { const int CONTROL_HARD_RESET_CLIENT_V2 = 7; + const int CONTROL_HARD_RESET_CLIENT_V3 = 10; const int OPCODE_SHIFT = 3; const int MIN_INITIAL_PKT_SIZE = 14; @@ -42,7 +43,8 @@ namespace openvpn { case 3: return p[0] == 0 && p[1] >= MIN_INITIAL_PKT_SIZE - && p[2] == (CONTROL_HARD_RESET_CLIENT_V2 << OPCODE_SHIFT); + && (p[2] == (CONTROL_HARD_RESET_CLIENT_V2 << OPCODE_SHIFT) + || p[2] == (CONTROL_HARD_RESET_CLIENT_V3 << OPCODE_SHIFT)); case 2: return p[0] == 0 && p[1] >= MIN_INITIAL_PKT_SIZE; case 1: diff --git a/openvpn/ssl/proto.hpp b/openvpn/ssl/proto.hpp index e3c1b5e..03d91e7 100644 --- a/openvpn/ssl/proto.hpp +++ b/openvpn/ssl/proto.hpp @@ -48,6 +48,10 @@ #include #include #include +#include +#include +#include +#include #include #include #include @@ -57,6 +61,7 @@ #include #include #include +#include #include #include #include @@ -65,6 +70,8 @@ #include #include #include +#include +#include #include #include #include @@ -171,6 +178,7 @@ namespace openvpn { // indicates key_method >= 2 CONTROL_HARD_RESET_CLIENT_V2 = 7, // initial key from client, forget previous state + CONTROL_HARD_RESET_CLIENT_V3 = 10, // initial key from client, forget previous state CONTROL_HARD_RESET_SERVER_V2 = 8, // initial key from server, forget previous state // define the range of legal opcodes @@ -291,8 +299,10 @@ namespace openvpn { // compressor CompressContext comp_ctx; - // tls_auth parms + // tls_auth/crypt parms OpenVPNStaticKey tls_key; // leave this undefined to disable tls_auth/crypt + bool tls_crypt_v2 = false; // needed to distinguish between tls-crypt and tls-crypt-v2 server mode + BufferAllocated wkc; // leave this undefined to disable tls-crypt-v2 on client OvpnHMACFactory::Ptr tls_auth_factory; OvpnHMACContext::Ptr tls_auth_context; @@ -301,6 +311,8 @@ namespace openvpn { TLSCryptFactory::Ptr tls_crypt_factory; TLSCryptContext::Ptr tls_crypt_context; + TLSCryptMetadataFactory::Ptr tls_crypt_metadata_factory; + // reliability layer parms reliable::id_t reliable_window = 0; size_t max_ack_list = 0; @@ -332,13 +344,15 @@ namespace openvpn { // MTU unsigned int tun_mtu = 1500; + MSSParms mss_parms; + unsigned int mss_inter = 0; // Debugging int debug_level = 1; // Compatibility bool force_aes_cbc_ciphersuites = false; - + // For compatibility with openvpn2 we send initial options on rekeying, // instead of possible modifications caused by NCP std::string initial_options; @@ -433,6 +447,8 @@ namespace openvpn { { if (tls_auth_context) throw proto_option_error("tls-auth and tls-crypt are mutually exclusive"); + if (tls_crypt_context) + throw proto_option_error("tls-crypt and tls-crypt-v2 are mutually exclusive"); tls_key.parse(o->get(1, 0)); @@ -445,6 +461,46 @@ namespace openvpn { set_tls_crypt_algs(digest, cipher); } } + + // tls-crypt-v2 + { + const Option *o = opt.get_ptr(relay_prefix("tls-crypt-v2")); + if (o) + { + if (tls_auth_context) + throw proto_option_error("tls-auth and tls-crypt-v2 are mutually exclusive"); + if (tls_crypt_context) + throw proto_option_error("tls-crypt and tls-crypt-v2 are mutually exclusive"); + + digest = CryptoAlgs::lookup("SHA256"); + cipher = CryptoAlgs::lookup("AES-256-CTR"); + + if ((digest == CryptoAlgs::NONE) || (cipher == CryptoAlgs::NONE)) + throw proto_option_error("missing support for tls-crypt-v2 algorithms"); + + // initialize tls_crypt_context + set_tls_crypt_algs(digest, cipher); + + std::string keyfile = o->get(1, 0); + + if (opt.exists("client")) + { + // in client mode expect the key to be a PEM encoded tls-crypt-v2 client key (key + WKc) + TLSCryptV2ClientKey tls_crypt_v2_key(tls_crypt_context); + tls_crypt_v2_key.parse(keyfile); + tls_crypt_v2_key.extract_key(tls_key); + tls_crypt_v2_key.extract_wkc(wkc); + } + else + { + // in server mode this is a PEM encoded tls-crypt-v2 server key + TLSCryptV2ServerKey tls_crypt_v2_key; + tls_crypt_v2_key.parse(keyfile); + tls_crypt_v2_key.extract_key(tls_key); + } + tls_crypt_v2 = true; + } + } } // key-direction @@ -507,6 +563,9 @@ namespace openvpn { // tun-mtu tun_mtu = parse_tun_mtu(opt, tun_mtu); + // mssfix + mss_parms.parse(opt); + // load parameters that can be present in both config file or pushed options load_common(opt, pco, server ? LOAD_COMMON_SERVER : LOAD_COMMON_CLIENT); } @@ -680,13 +739,18 @@ namespace openvpn { return tls_key.defined() && tls_crypt_context; } + bool tls_crypt_v2_enabled() const + { + return tls_crypt_enabled() && tls_crypt_v2; + } + // generate a string summarizing options that will be // transmitted to peer for options consistency check std::string options_string() { if (!initial_options.empty()) return initial_options; - + std::ostringstream out; const bool server = ssl_factory->mode().is_server(); @@ -726,7 +790,7 @@ namespace openvpn { out << ",tls-server"; else out << ",tls-client"; - + initial_options = out.str(); return initial_options; @@ -892,6 +956,7 @@ namespace openvpn { break; } case CONTROL_HARD_RESET_CLIENT_V2: + case CONTROL_HARD_RESET_CLIENT_V3: { if (!proto.is_server()) return; @@ -946,6 +1011,8 @@ namespace openvpn { return "DATA_V2"; case CONTROL_HARD_RESET_CLIENT_V2: return "CONTROL_HARD_RESET_CLIENT_V2"; + case CONTROL_HARD_RESET_CLIENT_V3: + return "CONTROL_HARD_RESET_CLIENT_V3"; case CONTROL_HARD_RESET_SERVER_V2: return "CONTROL_HARD_RESET_SERVER_V2"; } @@ -1217,6 +1284,8 @@ namespace openvpn { public: typedef RCPtr Ptr; + OPENVPN_SIMPLE_EXCEPTION(tls_crypt_unwrap_wkc_error); + // KeyContext events occur on two basic key types: // Primary Key -- the key we transmit/encrypt on. // Secondary Key -- new keys and retiring keys. @@ -1351,7 +1420,7 @@ namespace openvpn { send_reset(); set_state(state+1); dirty = true; - } + } } // control channel flush @@ -1466,6 +1535,10 @@ namespace openvpn { // decompress packet if (compress) compress->decompress(buf); + + // set MSS for segments server can receive + if (proto.config->mss_inter > 0) + MSSFix::mssfix(buf, proto.config->mss_inter); } else buf.reset_size(); // no crypto context available @@ -1607,6 +1680,15 @@ namespace openvpn { { case TLS_AUTH: return validate_tls_auth(recv, proto, now); + case TLS_CRYPT_V2: + if (opcode_extract(recv[0]) == CONTROL_HARD_RESET_CLIENT_V3) + { + // skip validation of HARD_RESET_V3 because the tls-crypt + // engine has not been initialized yet + OPENVPN_LOG_PROTO_VERBOSE("SKIPPING VALIDATION OF HARD_RESET_V3"); + return true; + } + /* no break */ case TLS_CRYPT: return validate_tls_crypt(recv, proto, now); case TLS_PLAIN: @@ -1675,6 +1757,35 @@ 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); + } } } @@ -1765,7 +1876,9 @@ namespace openvpn { work.inc_size(decrypt_bytes); // verify HMAC - if (!proto.tls_crypt_recv->hmac_cmp(orig_data, orig_size, work.c_data(), work.size())) + if (!proto.tls_crypt_recv->hmac_cmp(orig_data, + TLSCryptContext::hmac_offset, + work.c_data(), work.size())) return false; // verify source PSID @@ -1819,6 +1932,10 @@ 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); + // compress packet if (compress) compress->compress(buf, compress_hint); @@ -1990,25 +2107,36 @@ namespace openvpn { set_event(ev); } - unsigned int initial_op(const bool sender) const + unsigned int initial_op(const bool sender, const bool tls_crypt_v2) const { if (key_id_) - return CONTROL_SOFT_RESET_V1; + { + return CONTROL_SOFT_RESET_V1; + } else - return (proto.is_server() == sender) ? CONTROL_HARD_RESET_SERVER_V2 : CONTROL_HARD_RESET_CLIENT_V2; + { + if (proto.is_server() == sender) + return CONTROL_HARD_RESET_SERVER_V2; + + if (!tls_crypt_v2) + return CONTROL_HARD_RESET_CLIENT_V2; + else + return CONTROL_HARD_RESET_CLIENT_V3; + } } void send_reset() { Packet pkt; - pkt.opcode = initial_op(true); + pkt.opcode = initial_op(true, proto.tls_wrap_mode == TLS_CRYPT_V2); pkt.frame_prepare(*proto.config->frame, Frame::WRITE_SSL_INIT); raw_send(std::move(pkt)); } void raw_recv(Packet&& raw_pkt) // called by ProtoStackBase { - if (raw_pkt.buf->empty() && raw_pkt.opcode == initial_op(false)) + if (raw_pkt.buf->empty() && + raw_pkt.opcode == initial_op(false, proto.tls_wrap_mode == TLS_CRYPT_V2)) { switch (state) { @@ -2265,14 +2393,15 @@ namespace openvpn { // write opcode work.push_front(op_compose(opcode, key_id_)); - // compute HMAC using header fields (from 'work') and plaintext payload (from 'buf') - proto.tls_crypt_send->hmac_gen(work.data(), work.size(), buf.c_data(), buf.size()); + // compute HMAC using header fields (from 'work') and plaintext + // payload (from 'buf') + proto.tls_crypt_send->hmac_gen(work.data(), TLSCryptContext::hmac_offset, + buf.c_data(), buf.size()); - const size_t head_size = 1 + ProtoSessionID::SIZE + PacketID::size(PacketID::LONG_FORM); - const size_t data_offset = head_size + proto.hmac_size; + const size_t data_offset = TLSCryptContext::hmac_offset + proto.hmac_size; // encrypt the content of 'buf' (packet payload) into 'work' - const size_t decrypt_bytes = proto.tls_crypt_send->encrypt(work.c_data() + head_size, + const size_t decrypt_bytes = proto.tls_crypt_send->encrypt(work.c_data() + TLSCryptContext::hmac_offset, work.data() + data_offset, work.max_size() - data_offset, buf.c_data(), buf.size()); @@ -2283,6 +2412,11 @@ namespace openvpn { } work.inc_size(decrypt_bytes); + // append WKc to wrapped packet for tls-crypt-v2 + if ((opcode == CONTROL_HARD_RESET_CLIENT_V3) + && (proto.tls_wrap_mode == TLS_CRYPT_V2)) + proto.tls_crypt_append_wkc(work); + // 'work' now contains the complete packet ready to go. swap it with 'buf' buf.swap(work); } @@ -2303,6 +2437,7 @@ namespace openvpn { gen_head_tls_auth(opcode, buf); break; case TLS_CRYPT: + case TLS_CRYPT_V2: gen_head_tls_crypt(opcode, buf); break; case TLS_PLAIN: @@ -2448,15 +2583,14 @@ namespace openvpn { // skip the hmac recv.advance(proto.hmac_size); - const size_t head_size = 1 + ProtoSessionID::SIZE + PacketID::size(PacketID::LONG_FORM); - const size_t data_offset = head_size + proto.hmac_size; + const size_t data_offset = TLSCryptContext::hmac_offset + proto.hmac_size; if (orig_size < data_offset) return false; // decrypt payload proto.config->frame->prepare(Frame::DECRYPT_WORK, work); - const size_t decrypt_bytes = proto.tls_crypt_recv->decrypt(orig_data + head_size, + const size_t decrypt_bytes = proto.tls_crypt_recv->decrypt(orig_data + TLSCryptContext::hmac_offset, work.data(), work.max_size(), recv.c_data(), recv.size()); if (!decrypt_bytes) @@ -2470,7 +2604,8 @@ namespace openvpn { work.inc_size(decrypt_bytes); // verify HMAC - if (!proto.tls_crypt_recv->hmac_cmp(orig_data, orig_size, work.c_data(), work.size())) + if (!proto.tls_crypt_recv->hmac_cmp(orig_data, TLSCryptContext::hmac_offset, + work.c_data(), work.size())) { proto.stats->error(Error::HMAC_ERROR); if (proto.is_tcp()) @@ -2528,6 +2663,100 @@ namespace openvpn { return false; } + bool unwrap_tls_crypt_wkc(Buffer &recv) + { + // the ``WKc`` is located at the end of the packet, after the tls-crypt + // payload. + // Format is as follows (as documented by Steffan Krager): + // + // ``len = len(WKc)`` (16 bit, network byte order) + // ``T = HMAC-SHA256(Ka, len || Kc || metadata)`` + // ``IV = 128 most significant bits of T`` + // ``WKc = T || AES-256-CTR(Ke, IV, Kc || metadata) || len`` + + const unsigned char *orig_data = recv.data(); + const size_t orig_size = recv.size(); + const size_t hmac_size = proto.config->tls_crypt_context->digest_size(); + const size_t tls_frame_size = 1 + ProtoSessionID::SIZE + + PacketID::size(PacketID::LONG_FORM) + + hmac_size + + // the following is the tls-crypt payload + sizeof(char) + // length of ACK array + sizeof(id_t); // reliable ID + + // check that at least the authentication tag ``T`` is present + if (orig_size < (tls_frame_size + hmac_size)) + return false; + + // the ``WKc`` is just appended after the standard tls-crypt frame + const unsigned char *wkc_raw = orig_data + tls_frame_size; + const size_t wkc_raw_size = orig_size - tls_frame_size - sizeof(uint16_t); + // retrieve the ``WKc`` len from the bottom of the packet and convert it to Host Order + uint16_t wkc_len = ntohs(*(uint16_t *)(wkc_raw + wkc_raw_size)); + // length sanity check (the size of the ``len`` field is included in the value) + if ((wkc_len - sizeof(uint16_t)) != wkc_raw_size) + return false; + + BufferAllocated plaintext(wkc_len, BufferAllocated::CONSTRUCT_ZERO); + // plaintext will be used to compute the Auth Tag, therefore start by prepnding + // the WKc length in network order + wkc_len = htons(wkc_len); + plaintext.write(&wkc_len, sizeof(wkc_len)); + const size_t decrypt_bytes = proto.tls_crypt_server->decrypt(wkc_raw, + plaintext.data() + 2, + plaintext.max_size() - 2, + wkc_raw + hmac_size, + wkc_raw_size - hmac_size); + plaintext.inc_size(decrypt_bytes); + // decrypted data must at least contain a full 2048bits client key + // (metadata is optional) + if (plaintext.size() < OpenVPNStaticKey::KEY_SIZE) + { + proto.stats->error(Error::DECRYPT_ERROR); + if (proto.is_tcp()) + invalidate(Error::DECRYPT_ERROR); + return false; + } + + if (!proto.tls_crypt_server->hmac_cmp(wkc_raw, 0, + plaintext.c_data(), + plaintext.size())) + { + proto.stats->error(Error::HMAC_ERROR); + if (proto.is_tcp()) + invalidate(Error::HMAC_ERROR); + return false; + } + + // we can now remove the WKc length from the plaintext, as it is not + // really part of the key material + plaintext.advance(sizeof(wkc_len)); + + // WKc has been authenticated: it contains the client key followed + // by the optional metadata. Let's initialize the tls-crypt context + // with the client key + + OpenVPNStaticKey client_key; + plaintext.read(client_key.raw_alloc(), OpenVPNStaticKey::KEY_SIZE); + proto.reset_tls_crypt(*proto.config, client_key); + + // verify metadata + int metadata_type = -1; + if (!plaintext.empty()) + metadata_type = plaintext.pop_front(); + + if (!proto.tls_crypt_metadata->verify(metadata_type, plaintext)) + { + proto.stats->error(Error::TLS_CRYPT_META_FAIL); + return false; + } + + // virtually remove the WKc from the packet + recv.set_size(tls_frame_size); + + return true; + } + bool decapsulate(Packet& pkt) // called by ProtoStackBase { try { @@ -2535,6 +2764,20 @@ namespace openvpn { { case TLS_AUTH: return decapsulate_tls_auth(pkt); + case TLS_CRYPT_V2: + if (pkt.opcode == CONTROL_HARD_RESET_CLIENT_V3) + { + // unwrap WKc and extract Kc (client key) from packet. + // This way we can initialize the tls-crypt per-client contexts + // (this happens on the server side only) + if (!unwrap_tls_crypt_wkc(*pkt.buf)) + { + return false; + } + } + // now that the tls-crypt contexts have been initialized it is + // possible to proceed with the standard tls-crypt decapsulation + /* no break */ case TLS_CRYPT: return decapsulate_tls_crypt(pkt); case TLS_PLAIN: @@ -2724,15 +2967,14 @@ namespace openvpn { if (opcode_extract(op) != reset_op || key_id_extract(op) != 0) return false; - const size_t head_size = 1 + ProtoSessionID::SIZE + PacketID::size(PacketID::LONG_FORM); - const size_t data_offset = head_size + tls_crypt_recv->output_hmac_size(); + const size_t data_offset = TLSCryptContext::hmac_offset + tls_crypt_recv->output_hmac_size(); if (net_buf.size() < data_offset) return false; frame->prepare(Frame::DECRYPT_WORK, work); // decrypt payload from 'net_buf' into 'work' - const size_t decrypt_bytes = tls_crypt_recv->decrypt(net_buf.c_data() + head_size, + const size_t decrypt_bytes = tls_crypt_recv->decrypt(net_buf.c_data() + TLSCryptContext::hmac_offset, work.data(), work.max_size(), net_buf.c_data() + data_offset, net_buf.size() - data_offset); @@ -2742,7 +2984,8 @@ namespace openvpn { work.inc_size(decrypt_bytes); // verify HMAC - return tls_crypt_recv->hmac_cmp(net_buf.c_data(), net_buf.size(), + return tls_crypt_recv->hmac_cmp(net_buf.c_data(), + TLSCryptContext::hmac_offset, work.data(), work.size()); } catch (BufferException&) @@ -2751,11 +2994,30 @@ namespace openvpn { return false; } + protected: + unsigned int reset_op; + private: TLSCryptInstance::Ptr tls_crypt_recv; Frame::Ptr frame; BufferAllocated work; - unsigned int reset_op; + }; + + class TLSCryptV2PreValidate : public TLSCryptPreValidate + { + public: + OPENVPN_SIMPLE_EXCEPTION(tls_crypt_v2_pre_validate); + + TLSCryptV2PreValidate(const Config& c, const bool server) + : TLSCryptPreValidate(c, server) + { + if (!c.tls_crypt_v2_enabled()) + throw tls_crypt_v2_pre_validate(); + + // in case of server peer, we expect the new v3 packet type + if (server) + reset_op = CONTROL_HARD_RESET_CLIENT_V3; + } }; OPENVPN_SIMPLE_EXCEPTION(select_key_context_error); @@ -2771,20 +3033,27 @@ namespace openvpn { const Config& c = *config; // tls-auth setup - if (c.tls_auth_context) + if (c.tls_crypt_v2_enabled()) { - tls_wrap_mode = TLS_AUTH; + tls_wrap_mode = TLS_CRYPT_V2; // get HMAC size from Digest object - hmac_size = c.tls_auth_context->size(); + hmac_size = c.tls_crypt_context->digest_size(); } - else if (c.tls_crypt_context) + else if (c.tls_crypt_enabled()) { tls_wrap_mode = TLS_CRYPT; // get HMAC size from Digest object hmac_size = c.tls_crypt_context->digest_size(); } + else if (c.tls_auth_enabled()) + { + tls_wrap_mode = TLS_AUTH; + + // get HMAC size from Digest object + hmac_size = c.tls_auth_context->size(); + } else { tls_wrap_mode = TLS_PLAIN; @@ -2801,6 +3070,43 @@ namespace openvpn { return 0; } + void reset_tls_crypt(const Config& c, const OpenVPNStaticKey& key) + { + tls_crypt_send = c.tls_crypt_context->new_obj_send(); + tls_crypt_recv = c.tls_crypt_context->new_obj_recv(); + + // static direction assignment - not user configurable + unsigned int key_dir = is_server() ? + OpenVPNStaticKey::NORMAL : + OpenVPNStaticKey::INVERSE; + + tls_crypt_send->init(key.slice(OpenVPNStaticKey::HMAC | + OpenVPNStaticKey::ENCRYPT | key_dir), + key.slice(OpenVPNStaticKey::CIPHER | + OpenVPNStaticKey::ENCRYPT | key_dir)); + tls_crypt_recv->init(key.slice(OpenVPNStaticKey::HMAC | + OpenVPNStaticKey::DECRYPT | key_dir), + key.slice(OpenVPNStaticKey::CIPHER | + OpenVPNStaticKey::DECRYPT | key_dir)); + } + + void reset_tls_crypt_server(const Config& c) + { + //tls-crypt session key is derived later from WKc received from the client + tls_crypt_send.reset(); + tls_crypt_recv.reset(); + + //server context is used only to process incoming WKc's + tls_crypt_server = c.tls_crypt_context->new_obj_recv(); + + //the server key is composed by one key set only, therefore direction and + //mode should not be specified when slicing + tls_crypt_server->init(c.tls_key.slice(OpenVPNStaticKey::HMAC), + c.tls_key.slice(OpenVPNStaticKey::CIPHER)); + + tls_crypt_metadata = c.tls_crypt_metadata_factory->new_obj(); + } + void reset() { const Config& c = *config; @@ -2820,16 +3126,19 @@ namespace openvpn { switch (tls_wrap_mode) { case TLS_CRYPT: - tls_crypt_send = c.tls_crypt_context->new_obj_send(); - tls_crypt_recv = c.tls_crypt_context->new_obj_recv(); - - // static direction assignment - not user configurable - key_dir = is_server() ? OpenVPNStaticKey::NORMAL : OpenVPNStaticKey::INVERSE; - tls_crypt_send->init(c.tls_key.slice(OpenVPNStaticKey::HMAC | OpenVPNStaticKey::ENCRYPT | key_dir), - c.tls_key.slice(OpenVPNStaticKey::CIPHER | OpenVPNStaticKey::ENCRYPT | key_dir)); - tls_crypt_recv->init(c.tls_key.slice(OpenVPNStaticKey::HMAC | OpenVPNStaticKey::DECRYPT | key_dir), - c.tls_key.slice(OpenVPNStaticKey::CIPHER | OpenVPNStaticKey::DECRYPT | key_dir)); - + reset_tls_crypt(c, c.tls_key); + // init tls_crypt packet ID + ta_pid_send.init(PacketID::LONG_FORM); + ta_pid_recv.init(c.pid_mode, PacketID::LONG_FORM, "SSL-CC", 0, stats); + break; + case TLS_CRYPT_V2: + if (is_server()) + // setup key to be used to unwrap WKc upon client connection. + // tls-crypt session key setup is postponed to reception of WKc + // from client + reset_tls_crypt_server(c); + else + reset_tls_crypt(c, c.tls_key); // init tls_crypt packet ID ta_pid_send.init(PacketID::LONG_FORM); ta_pid_recv.init(c.pid_mode, PacketID::LONG_FORM, "SSL-CC", 0, stats); @@ -2860,7 +3169,7 @@ namespace openvpn { break; case TLS_PLAIN: break; - } + } // initialize proto session ID psid_self.randomize(*c.prng); @@ -3199,7 +3508,8 @@ namespace openvpn { enum TLSWrapMode { TLS_PLAIN, TLS_AUTH, - TLS_CRYPT + TLS_CRYPT, + TLS_CRYPT_V2 }; void reset_all() @@ -3483,6 +3793,13 @@ namespace openvpn { keepalive_xmit = kx; } + void tls_crypt_append_wkc(BufferAllocated& dst) + { + if (!config->wkc.defined()) + throw proto_error("Client Key Wrapper undefined"); + dst.append(config->wkc); + } + // BEGIN ProtoContext data members Config::Ptr config; @@ -3506,6 +3823,9 @@ namespace openvpn { TLSCryptInstance::Ptr tls_crypt_send; TLSCryptInstance::Ptr tls_crypt_recv; + TLSCryptInstance::Ptr tls_crypt_server; + TLSCryptMetadata::Ptr tls_crypt_metadata; + PacketIDSend ta_pid_send; PacketIDReceive ta_pid_recv; diff --git a/openvpn/ssl/sslchoose.hpp b/openvpn/ssl/sslchoose.hpp index 285260e..eb07766 100644 --- a/openvpn/ssl/sslchoose.hpp +++ b/openvpn/ssl/sslchoose.hpp @@ -26,6 +26,7 @@ #include #include #include +#include #endif #ifdef USE_APPLE_SSL @@ -46,6 +47,7 @@ #ifdef OPENVPN_PLATFORM_UWP #include #endif +#include #endif #ifdef USE_MBEDTLS_APPLE_HYBRID @@ -69,6 +71,7 @@ namespace openvpn { #else typedef MbedTLSRandom RandomAPI; #endif + typedef MbedTLSPEM PEMAPI; #elif defined(USE_MBEDTLS_APPLE_HYBRID) // Uses Apple framework for CryptoAPI and MbedTLS for SSLAPI and RandomAPI #define SSL_LIB_NAME "MbedTLSAppleHybrid" @@ -85,6 +88,7 @@ namespace openvpn { typedef OpenSSLCryptoAPI CryptoAPI; typedef OpenSSLContext SSLAPI; typedef OpenSSLRandom RandomAPI; + typedef OpenSSLPEM PEMAPI; #else #error no SSL library defined #endif diff --git a/openvpn/time/asiotimersafe.hpp b/openvpn/time/asiotimersafe.hpp new file mode 100644 index 0000000..d277072 --- /dev/null +++ b/openvpn/time/asiotimersafe.hpp @@ -0,0 +1,78 @@ +// OpenVPN -- An application to securely tunnel IP networks +// over a single port, with support for SSL/TLS-based +// session authentication and key exchange, +// packet encryption, packet authentication, and +// packet compression. +// +// Copyright (C) 2012-2017 OpenVPN Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License Version 3 +// 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program in the COPYING file. +// If not, see . + +#pragma once + +#include + +// AsioTimerSafe is like AsioTimer but with an epoch counter +// that allows a handler to determine if it is the most recent +// handler to be queued. + +namespace openvpn { + class AsioTimerSafe + { + public: + typedef std::size_t epoch_t; + + AsioTimerSafe(openvpn_io::io_context& io_context) + : timer_(io_context) + { + } + + std::size_t expires_at(const Time& t) + { + ++epoch_; + return timer_.expires_at(t); + } + + std::size_t expires_after(const Time::Duration& d) + { + ++epoch_; + return timer_.expires_after(d); + } + + std::size_t cancel() + { + ++epoch_; + return timer_.cancel(); + } + + epoch_t epoch() const + { + return epoch_; + } + + template + void async_wait(F&& func) + { + ++epoch_; + timer_.async_wait([func=std::move(func), epoch=epoch_](const openvpn_io::error_code& error) mutable + { + func(error, epoch); + }); + } + + private: + AsioTimer timer_; + epoch_t epoch_ = 0; + }; +} diff --git a/openvpn/transport/client/httpcli.hpp b/openvpn/transport/client/httpcli.hpp index 5b48629..8b7f15d 100644 --- a/openvpn/transport/client/httpcli.hpp +++ b/openvpn/transport/client/httpcli.hpp @@ -916,7 +916,7 @@ namespace openvpn { #ifdef OPENVPN_PLATFORM_TYPE_UNIX if (config->socket_protect) { - if (!config->socket_protect->socket_protect(socket.native_handle())) + if (!config->socket_protect->socket_protect(socket.native_handle(), server_endpoint_addr())) { config->stats->error(Error::SOCKET_PROTECT_ERROR); stop(); diff --git a/openvpn/transport/client/tcpcli.hpp b/openvpn/transport/client/tcpcli.hpp index 3a273bf..a1d3831 100644 --- a/openvpn/transport/client/tcpcli.hpp +++ b/openvpn/transport/client/tcpcli.hpp @@ -287,7 +287,7 @@ namespace openvpn { #if defined(OPENVPN_PLATFORM_TYPE_UNIX) || defined(OPENVPN_PLATFORM_UWP) if (config->socket_protect) { - if (!config->socket_protect->socket_protect(socket.native_handle())) + if (!config->socket_protect->socket_protect(socket.native_handle(), server_endpoint_addr())) { config->stats->error(Error::SOCKET_PROTECT_ERROR); stop(); diff --git a/openvpn/transport/client/transbase.hpp b/openvpn/transport/client/transbase.hpp index a0183ae..18f9451 100644 --- a/openvpn/transport/client/transbase.hpp +++ b/openvpn/transport/client/transbase.hpp @@ -91,6 +91,8 @@ namespace openvpn { // the keepalive parameters (in seconds). virtual void disable_keepalive(unsigned int& keepalive_ping, unsigned int& keepalive_timeout) = 0; + + virtual ~TransportClientParent() {} }; // Factory for client transport object. diff --git a/openvpn/transport/client/udpcli.hpp b/openvpn/transport/client/udpcli.hpp index e55e7c6..b5887b2 100644 --- a/openvpn/transport/client/udpcli.hpp +++ b/openvpn/transport/client/udpcli.hpp @@ -181,8 +181,7 @@ namespace openvpn { Client(openvpn_io::io_context& io_context_arg, ClientConfig* config_arg, TransportClientParent* parent_arg) - : io_context(io_context_arg), - socket(io_context_arg), + : socket(io_context_arg), config(config_arg), parent(parent_arg), resolver(io_context_arg), @@ -274,7 +273,7 @@ namespace openvpn { #if defined(OPENVPN_PLATFORM_TYPE_UNIX) || defined(OPENVPN_PLATFORM_UWP) if (config->socket_protect) { - if (!config->socket_protect->socket_protect(socket.native_handle())) + if (!config->socket_protect->socket_protect(socket.native_handle(), server_endpoint_addr())) { config->stats->error(Error::SOCKET_PROTECT_ERROR); stop(); @@ -321,7 +320,6 @@ namespace openvpn { std::string server_host; std::string server_port; - openvpn_io::io_context& io_context; openvpn_io::ip::udp::socket socket; ClientConfig::Ptr config; TransportClientParent* parent; diff --git a/openvpn/transport/mssfix.hpp b/openvpn/transport/mssfix.hpp new file mode 100644 index 0000000..de61519 --- /dev/null +++ b/openvpn/transport/mssfix.hpp @@ -0,0 +1,150 @@ +// OpenVPN -- An application to securely tunnel IP networks +// over a single port, with support for SSL/TLS-based +// session authentication and key exchange, +// packet encryption, packet authentication, and +// packet compression. +// +// Copyright (C) 2012-2018 OpenVPN Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License Version 3 +// 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program in the COPYING file. +// If not, see . + +#pragma once + +#include +#include +#include +#include +#include + +#if OPENVPN_DEBUG_PROTO >= 2 +#define OPENVPN_LOG_MSSFIX(x) OPENVPN_LOG(x) +#else +#define OPENVPN_LOG_MSSFIX(x) +#endif + +namespace openvpn { + class MSSFix { + public: + static void mssfix(BufferAllocated& buf, int mss_inter) + { + if (buf.empty()) + return; + + switch (IPCommon::version(buf[0])) + { + case IPCommon::IPv4: + { + if (buf.length() <= sizeof(struct IPv4Header)) + break; + + const IPv4Header *iphdr = (const IPv4Header *)buf.c_data(); + + auto ipv4hlen = IPv4Header::length(iphdr->version_len); + + if (iphdr->protocol == IPCommon::TCP && + ntohs(iphdr->tot_len) == buf.length() && + (ntohs(iphdr->frag_off) & IPv4Header::OFFMASK) == 0 && + ipv4hlen <= buf.length() && + buf.length() - ipv4hlen >= sizeof(struct TCPHeader)) + { + 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); + } + } + break; + + case IPCommon::IPv6: + { + if (buf.length() <= sizeof(struct IPv6Header)) + break; + + const IPv6Header *iphdr = (const IPv6Header *)buf.c_data(); + + if (buf.length() != ntohs(iphdr->payload_len) + sizeof(struct IPv6Header)) + break; + + /* follow header chain until we reach final header, then check for TCP + * + * An IPv6 packet could, theoretically, have a chain of multiple headers + * before the final header (TCP, UDP, ...), so we'd need to walk that + * chain (see RFC 2460 and RFC 6564 for details). + * + * In practice, "most typically used" extention headers (AH, routing, + * fragment, mobility) are very unlikely to be seen inside an OpenVPN + * tun, so for now, we only handle the case of "single next header = TCP" + */ + if (iphdr->nexthdr != IPCommon::TCP) + break; + + /* skip IPv6 header (40 bytes), + * verify remainder is large enough to contain a full TCP header + */ + int payload_len = buf.length() - sizeof(struct IPv6Header); + 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); + } + } + break; + } + } + + private: + static void do_mssfix(TCPHeader *tcphdr, int max_mss, int ip_payload_len) + { + if ((tcphdr->flags & TCPHeader::FLAG_SYN) == 0) + return; + + int tcphlen = TCPHeader::length(tcphdr->doff_res); + if (tcphlen <= (int) sizeof(struct TCPHeader) || tcphlen > ip_payload_len) + return; + + int olen, optlen; // length of options field and Option-Length + uint8_t *opt; // option type + + for (olen = tcphlen - sizeof(struct TCPHeader), opt = (uint8_t *)(tcphdr + 1); + olen > 1; + olen -= optlen, opt += optlen) + { + if (*opt == TCPHeader::OPT_EOL) + break; + else if (*opt == TCPHeader::OPT_NOP) + optlen = 1; + else + { + optlen = *(opt + 1); + if (optlen <= 0 || optlen > olen) + break; + if ((*opt == TCPHeader::OPT_MAXSEG) && (optlen == TCPHeader::OPTLEN_MAXSEG)) + { + uint16_t mssval = (opt[2] << 8) + opt[3]; + if (mssval > max_mss) + { + OPENVPN_LOG_MSSFIX("MTU MSS " << mssval << " -> " << max_mss); + int accumulate = htons(mssval); + opt[2] = (max_mss >> 8) & 0xff; + opt[3] = max_mss & 0xff; + accumulate -= htons(max_mss); + tcp_adjust_checksum(accumulate, tcphdr->check); + } + } + } + } + } + }; +} diff --git a/openvpn/transport/socket_protect.hpp b/openvpn/transport/socket_protect.hpp index 97aef72..6552498 100644 --- a/openvpn/transport/socket_protect.hpp +++ b/openvpn/transport/socket_protect.hpp @@ -22,6 +22,7 @@ #ifndef OPENVPN_TRANSPORT_SOCKET_PROTECT_H #define OPENVPN_TRANSPORT_SOCKET_PROTECT_H +#include #ifdef OPENVPN_PLATFORM_UWP #include #endif @@ -33,7 +34,7 @@ namespace openvpn { // the socket from being routed into the VPN tunnel. class BaseSocketProtect { public: - virtual bool socket_protect(int socket) = 0; + virtual bool socket_protect(int socket, IP::Addr endpoint) = 0; }; #ifdef OPENVPN_PLATFORM_UWP diff --git a/openvpn/tun/builder/capture.hpp b/openvpn/tun/builder/capture.hpp index 209429c..524e044 100644 --- a/openvpn/tun/builder/capture.hpp +++ b/openvpn/tun/builder/capture.hpp @@ -342,7 +342,7 @@ namespace openvpn { { try { if (defined()) - URL::Parse(url); + (URL::Parse(url)); } catch (const std::exception& e) { diff --git a/openvpn/tun/client/tunprop.hpp b/openvpn/tun/client/tunprop.hpp index 5631e1e..c18b407 100644 --- a/openvpn/tun/client/tunprop.hpp +++ b/openvpn/tun/client/tunprop.hpp @@ -485,7 +485,7 @@ namespace openvpn { const Option& o = opt[*i]; try { const std::string& type = o.get(1, 64); - if (type == "DNS") + if (type == "DNS" || type == "DNS6") { o.exact_args(3); const IP::Addr ip = IP::Addr::from_string(o.get(2, 256), "dns-server-ip"); diff --git a/openvpn/tun/linux/client/sitnl.hpp b/openvpn/tun/linux/client/sitnl.hpp new file mode 100644 index 0000000..975d6cb --- /dev/null +++ b/openvpn/tun/linux/client/sitnl.hpp @@ -0,0 +1,917 @@ +// OpenVPN -- An application to securely tunnel IP networks +// over a single port, with support for SSL/TLS-based +// session authentication and key exchange, +// packet encryption, packet authentication, and +// packet compression. +// +// Copyright (C) 2012-2018 OpenVPN Inc. +// Copyright (C) 2018 Antonio Quartulli +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License Version 3 +// 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program in the COPYING file. +// If not, see . + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +#ifdef DEBUG_RTNL +#define OPENVPN_LOG_RTNL(_x) OPENVPN_LOG(_x) +#else +#define OPENVPN_LOG_RTNL(_x) +#endif + +namespace openvpn { + namespace TunNetlink { + +#define SNDBUF_SIZE (1024 * 2) +#define RCVBUF_SIZE (1024 * 4) + +#define SITNL_ADDATTR(_msg, _max_size, _attr, _data, _size) \ + { \ + if (sitnl_addattr(_msg, _max_size, _attr, _data, _size) < 0)\ + { \ + goto err; \ + } \ + } + +#define NLMSG_TAIL(nmsg) \ + ((struct rtattr *)(((uint8_t *)(nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) + + /* this class contains only static members */ + class SITNL + { + private: + /** + * Link state request message + */ + struct sitnl_link_req + { + struct nlmsghdr n; + struct ifinfomsg i; + char buf[256]; + }; + + /** + * Address request message + */ + struct sitnl_addr_req + { + struct nlmsghdr n; + struct ifaddrmsg i; + char buf[256]; + }; + + /** + * Route request message + */ + struct sitnl_route_req + { + struct nlmsghdr n; + struct rtmsg r; + char buf[256]; + }; + + typedef int (*sitnl_parse_reply_cb)(struct nlmsghdr *msg, void *arg); + + /** + * Helper function used to easily add attributes to a rtnl message + */ + static int + sitnl_addattr(struct nlmsghdr *n, int maxlen, int type, const void *data, + int alen) + { + int len = RTA_LENGTH(alen); + struct rtattr *rta; + + if ((int)(NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len)) > maxlen) + { + OPENVPN_LOG(__func__ << ": rtnl: message exceeded bound of " << maxlen); + return -EMSGSIZE; + } + + rta = NLMSG_TAIL(n); + rta->rta_type = type; + rta->rta_len = len; + + if (!data) + { + memset(RTA_DATA(rta), 0, alen); + } + else + { + memcpy(RTA_DATA(rta), data, alen); + } + + n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len); + + return 0; + } + + /** + * Open RTNL socket + */ + static int + sitnl_socket(void) + { + int sndbuf = SNDBUF_SIZE; + int rcvbuf = RCVBUF_SIZE; + int fd; + + fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (fd < 0) + { + OPENVPN_LOG(__func__ << ": cannot open netlink socket"); + return fd; + } + + if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)) < 0) + { + OPENVPN_LOG(__func__ << ": SO_SNDBUF"); + close(fd); + return -1; + } + + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)) < 0) + { + OPENVPN_LOG(__func__ << ": SO_RCVBUF"); + close(fd); + return -1; + } + + return fd; + } + + /** + * Bind socket to Netlink subsystem + */ + static int + sitnl_bind(int fd, uint32_t groups) + { + socklen_t addr_len; + struct sockaddr_nl local = { }; + + local.nl_family = AF_NETLINK; + local.nl_groups = groups; + + if (bind(fd, (struct sockaddr *)&local, sizeof(local)) < 0) + { + OPENVPN_LOG(__func__ << ": cannot bind netlink socket"); + return -errno; + } + + addr_len = sizeof(local); + if (getsockname(fd, (struct sockaddr *)&local, &addr_len) < 0) + { + OPENVPN_LOG(__func__ << ": cannot getsockname"); + return -errno; + } + + if (addr_len != sizeof(local)) + { + OPENVPN_LOG(__func__ << ": wrong address length " << addr_len); + return -EINVAL; + } + + if (local.nl_family != AF_NETLINK) + { + OPENVPN_LOG(__func__ << ": wrong address family " << local.nl_family); + return -EINVAL; + } + + return 0; + } + + /** + * Send Netlink message and run callback on reply (if specified) + */ + static int + sitnl_send(struct nlmsghdr *payload, pid_t peer, unsigned int groups, + sitnl_parse_reply_cb cb, void *arg_cb) + { + int len, rem_len, fd, ret, rcv_len; + struct sockaddr_nl nladdr = { }; + struct nlmsgerr *err; + struct nlmsghdr *h; + unsigned int seq; + char buf[1024 * 16]; + struct iovec iov = + { + .iov_base = payload, + .iov_len = payload->nlmsg_len, + }; + struct msghdr nlmsg = + { + .msg_name = &nladdr, + .msg_namelen = sizeof(nladdr), + .msg_iov = &iov, + .msg_iovlen = 1, + }; + + nladdr.nl_family = AF_NETLINK; + nladdr.nl_pid = peer; + nladdr.nl_groups = groups; + + payload->nlmsg_seq = seq = time(NULL); + + /* no need to send reply */ + if (!cb) + { + payload->nlmsg_flags |= NLM_F_ACK; + } + + fd = sitnl_socket(); + if (fd < 0) + { + OPENVPN_LOG(__func__ << ": can't open rtnl socket"); + return -errno; + } + + ret = sitnl_bind(fd, 0); + if (ret < 0) + { + OPENVPN_LOG(__func__ << ": can't bind rtnl socket"); + ret = -errno; + goto out; + } + + ret = sendmsg(fd, &nlmsg, 0); + if (ret < 0) + { + OPENVPN_LOG(__func__ << ": rtnl: error on sendmsg()"); + ret = -errno; + goto out; + } + + /* prepare buffer to store RTNL replies */ + memset(buf, 0, sizeof(buf)); + iov.iov_base = buf; + + while (1) + { + /* + * iov_len is modified by recvmsg(), therefore has to be initialized before + * using it again + */ + OPENVPN_LOG_RTNL(__func__ << ": checking for received messages"); + iov.iov_len = sizeof(buf); + rcv_len = recvmsg(fd, &nlmsg, 0); + OPENVPN_LOG_RTNL(__func__ << ": rtnl: received " << rcv_len << " bytes"); + if (rcv_len < 0) + { + if ((errno == EINTR) || (errno == EAGAIN)) + { + OPENVPN_LOG(__func__ << ": interrupted call"); + continue; + } + OPENVPN_LOG(__func__ << ": rtnl: error on recvmsg()"); + ret = -errno; + goto out; + } + + if (rcv_len == 0) + { + OPENVPN_LOG(__func__ << ": rtnl: socket reached unexpected EOF"); + ret = -EIO; + goto out; + } + + if (nlmsg.msg_namelen != sizeof(nladdr)) + { + OPENVPN_LOG(__func__ << ": sender address length: " + << nlmsg.msg_namelen << " (expected " << sizeof(nladdr) + << ")"); + ret = -EIO; + goto out; + } + + h = (struct nlmsghdr *)buf; + while (rcv_len >= (int)sizeof(*h)) + { + len = h->nlmsg_len; + rem_len = len - sizeof(*h); + + if ((rem_len < 0) || (len > rcv_len)) + { + if (nlmsg.msg_flags & MSG_TRUNC) + { + OPENVPN_LOG(__func__ << ": truncated message"); + ret = -EIO; + goto out; + } + OPENVPN_LOG(__func__ << ": malformed message: len=" << len); + ret = -EIO; + goto out; + } + + if (h->nlmsg_type == NLMSG_ERROR) + { + err = (struct nlmsgerr *)NLMSG_DATA(h); + if (rem_len < (int)sizeof(struct nlmsgerr)) + { + OPENVPN_LOG(__func__ << ": ERROR truncated"); + ret = -EIO; + } + else + { + if (!err->error) + { + ret = 0; + if (cb) + ret = cb(h, arg_cb); + } + else + { + OPENVPN_LOG(__func__ << ": rtnl: generic error: " + << strerror(-err->error) + << " (" << err->error << ")"); + ret = err->error; + } + } + goto out; + } + + if (cb) + { + ret = cb(h, arg_cb); + goto out; + } + else + { + OPENVPN_LOG(__func__ << ": RTNL: unexpected reply"); + } + + rcv_len -= NLMSG_ALIGN(len); + h = (struct nlmsghdr *)((char *)h + NLMSG_ALIGN(len)); + } + + if (nlmsg.msg_flags & MSG_TRUNC) + { + OPENVPN_LOG(__func__ << ": message truncated"); + continue; + } + + if (rcv_len) + { + OPENVPN_LOG(__func__ << ": rtnl: " << rcv_len + << " not parsed bytes"); + ret = -1; + goto out; + } + } +out: + close(fd); + + return ret; + } + + /* store the route entry resulting from the query */ + typedef struct + { + sa_family_t family; + IP::Addr gw; + std::string iface; + } route_res_t; + + static int + sitnl_route_save(struct nlmsghdr *n, void *arg) + { + route_res_t *res = (route_res_t *)arg; + struct rtmsg *r = (struct rtmsg *)NLMSG_DATA(n); + struct rtattr *rta = RTM_RTA(r); + int len = n->nlmsg_len - NLMSG_LENGTH(sizeof(*r)); + int ifindex = 0; + + while (RTA_OK(rta, len)) + { + switch (rta->rta_type) + { + case RTA_OIF: + /* route interface */ + ifindex = *(unsigned int *)RTA_DATA(rta); + break; + case RTA_DST: + /* route prefix */ + RTA_DATA(rta); + break; + case RTA_GATEWAY: + /* GW for the route */ + { + const unsigned char *bytestr = (unsigned char *)RTA_DATA(rta); + switch (res->family) + { + case AF_INET: + res->gw = IP::Addr::from_ipv4(IPv4::Addr::from_bytes_net(bytestr)); + break; + case AF_INET6: + res->gw = IP::Addr::from_ipv6(IPv6::Addr::from_byte_string(bytestr)); + break; + } + } + break; + } + + rta = RTA_NEXT(rta, len); + } + + if (ifindex > 0) + { + char iface[IFNAMSIZ]; + if (!if_indextoname(ifindex, iface)) + { + OPENVPN_LOG(__func__ << ": rtnl: can't get ifname for index " + << ifindex); + return -1; + } + + res->iface = iface; + } + + return 0; + } + + static int + sitnl_route_best_gw(const IP::Route& route, IP::Addr& best_gw, + std::string& best_iface) + { + struct sitnl_route_req req = { }; + route_res_t res; + int ret = -EINVAL; + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.r)); + req.n.nlmsg_type = RTM_GETROUTE; + req.n.nlmsg_flags = NLM_F_REQUEST; + + res.family = req.r.rtm_family = route.addr.family(); + req.r.rtm_dst_len = route.prefix_len; + + if (route.addr.family() == AF_INET) + { + req.n.nlmsg_flags |= NLM_F_DUMP; + } + + { + unsigned char bytestr[IP::Addr::V6_SIZE / 8]; + route.addr.to_byte_string_variable(bytestr); + + SITNL_ADDATTR(&req.n, sizeof(req), RTA_DST, bytestr, + route.addr.size_bytes()); + } + + ret = sitnl_send(&req.n, 0, 0, sitnl_route_save, &res); + if (ret >= 0) + { + /* save result in output variables */ + best_gw = std::move(res.gw); + best_iface = std::move(res.iface); + + OPENVPN_LOG(__func__ << " result: via " << best_gw << " dev " << best_iface); + } + else + { + OPENVPN_LOG(__func__ << ": failed to retrieve route, err=" << ret); + } + +err: + 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, + const IP::Addr& broadcast) + { + struct sitnl_addr_req req = { }; + int ret = -EINVAL; + + if (iface.empty()) + { + OPENVPN_LOG(__func__ << ": passed empty interface"); + return -EINVAL; + } + + if (local.unspecified()) + { + OPENVPN_LOG(__func__ << ": passed zero IP address"); + return -EINVAL; + } + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.i)); + req.n.nlmsg_type = cmd; + req.n.nlmsg_flags = NLM_F_REQUEST | flags; + + req.i.ifa_family = local.family(); + req.i.ifa_index = if_nametoindex(iface.c_str()); + if (req.i.ifa_index == 0) + { + OPENVPN_LOG(__func__ << ": cannot get ifindex for " << iface << " " + << strerror(errno)); + return -ENOENT; + } + + /* if no prefixlen has been specified, assume host address */ + if (prefixlen == 0) + { + prefixlen = local.size(); + } + req.i.ifa_prefixlen = prefixlen; + + { + unsigned char bytestr[IP::Addr::V6_SIZE / 8]; + + local.to_byte_string_variable(bytestr); + SITNL_ADDATTR(&req.n, sizeof(req), IFA_LOCAL, bytestr, local.size_bytes()); + + if (remote.specified()) + { + remote.to_byte_string_variable(bytestr); + SITNL_ADDATTR(&req.n, sizeof(req), IFA_ADDRESS, bytestr, remote.size_bytes()); + } + + if (broadcast.specified()) + { + broadcast.to_byte_string_variable(bytestr); + SITNL_ADDATTR(&req.n, sizeof(req), IFA_BROADCAST, bytestr, broadcast.size_bytes()); + } + } + + ret = sitnl_send(&req.n, 0, 0, NULL, NULL); + if ((ret < 0) && (errno == EEXIST)) + { + ret = 0; + } + +err: + return ret; + } + + static int + sitnl_addr_ptp_add(const std::string& iface, const IP::Addr& local, + const IP::Addr& remote) + { + return sitnl_addr_set(RTM_NEWADDR, NLM_F_CREATE | NLM_F_REPLACE, iface, + local, remote, 0, + IP::Addr::from_zero(local.version())); + } + + static int + sitnl_addr_ptp_del(const std::string& iface, const IP::Addr& local) + { + return sitnl_addr_set(RTM_DELADDR, 0, iface, local, + IP::Addr::from_zero(local.version()), + 0, IP::Addr::from_zero(local.version())); + } + + static int + sitnl_route_set(const int cmd, const uint32_t flags, + const std::string& iface, const IP::Route& route, + const IP::Addr& gw, const enum rt_class_t table, + const int metric, const enum rt_scope_t scope, + const int protocol, const int type) + { + struct sitnl_route_req req = { }; + int ret = -1; + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.r)); + req.n.nlmsg_type = cmd; + req.n.nlmsg_flags = NLM_F_REQUEST | flags; + + req.r.rtm_family = route.addr.family(); + req.r.rtm_scope = scope; + req.r.rtm_protocol = protocol; + req.r.rtm_type = type; + req.r.rtm_dst_len = route.prefix_len; + + if (table < 256) + { + req.r.rtm_table = table; + } + else + { + req.r.rtm_table = RT_TABLE_UNSPEC; + SITNL_ADDATTR(&req.n, sizeof(req), RTA_TABLE, &table, 4); + } + + { + unsigned char bytestr[IP::Addr::V6_SIZE / 8]; + + route.addr.to_byte_string_variable(bytestr); + SITNL_ADDATTR(&req.n, sizeof(req), RTA_DST, bytestr, route.addr.size_bytes()); + + if (gw.specified()) + { + gw.to_byte_string_variable(bytestr); + SITNL_ADDATTR(&req.n, sizeof(req), RTA_GATEWAY, bytestr, gw.size_bytes()); + } + } + + if (!iface.empty()) + { + int ifindex = if_nametoindex(iface.c_str()); + if (ifindex == 0) + { + OPENVPN_LOG(__func__ << ": rtnl: cannot get ifindex for " << iface); + return -ENOENT; + } + + SITNL_ADDATTR(&req.n, sizeof(req), RTA_OIF, &ifindex, 4); + } + + if (metric > 0) + { + SITNL_ADDATTR(&req.n, sizeof(req), RTA_PRIORITY, &metric, 4); + } + + ret = sitnl_send(&req.n, 0, 0, NULL, NULL); + if ((ret < 0) && (errno == EEXIST)) + { + ret = 0; + } + +err: + return ret; + } + + static int + sitnl_addr_add(const std::string& iface, const IP::Addr& addr, + int prefixlen, const IP::Addr& broadcast) + { + return sitnl_addr_set(RTM_NEWADDR, NLM_F_CREATE | NLM_F_REPLACE, iface, + addr, IP::Addr::from_zero(addr.version()), + prefixlen, broadcast); + } + + static int + sitnl_addr_del(const std::string& iface, const IP::Addr& addr, int prefixlen) + { + return sitnl_addr_set(RTM_DELADDR, 0, iface, addr, + IP::Addr::from_zero(addr.version()), prefixlen, + IP::Addr::from_zero(addr.version())); + } + + static int + sitnl_route_add(const IP::Route& route, const IP::Addr& gw, + const std::string& iface, const uint32_t table, + const int metric) + { + return sitnl_route_set(RTM_NEWROUTE, NLM_F_CREATE | NLM_F_REPLACE, iface, + route, gw, + (enum rt_class_t)(!table ? RT_TABLE_MAIN : table), + metric, RT_SCOPE_UNIVERSE, RTPROT_BOOT, RTN_UNICAST); + } + + static int + sitnl_route_del(const IP::Route& route, const IP::Addr& gw, + const std::string& iface, const uint32_t table, + const int metric) + { + return sitnl_route_set(RTM_DELROUTE, 0, iface, route, gw, + (enum rt_class_t)(!table ? RT_TABLE_MAIN : table), + metric, RT_SCOPE_NOWHERE, + 0, 0); + } + + public: + + static int + net_route_best_gw(const IP::Route6& route, IPv6::Addr& best_gw6, + std::string& best_iface) + { + IP::Addr best_gw; + int ret; + + OPENVPN_LOG(__func__ << " query IPv6: " << route); + + ret = sitnl_route_best_gw(IP::Route(IP::Addr::from_ipv6(route.addr), route.prefix_len), + best_gw, best_iface); + if (ret >= 0) + { + best_gw6 = best_gw.to_ipv6(); + } + + return ret; + } + + static int + net_route_best_gw(const IP::Route4& route, IPv4::Addr &best_gw4, + std::string& best_iface) + { + IP::Addr best_gw; + int ret; + + OPENVPN_LOG(__func__ << " query IPv4: " << route); + + ret = sitnl_route_best_gw(IP::Route(IP::Addr::from_ipv4(route.addr), route.prefix_len), + best_gw, best_iface); + if (ret >= 0) + { + best_gw4 = best_gw.to_ipv4(); + } + + return ret; + } + + static int + net_iface_up(std::string& iface, bool up) + { + struct sitnl_link_req req = { }; + int ifindex; + + if (iface.empty()) + { + OPENVPN_LOG(__func__ << ": passed empty interface"); + return -EINVAL; + } + + ifindex = if_nametoindex(iface.c_str()); + if (ifindex == 0) + { + OPENVPN_LOG(__func__ << ": rtnl: cannot get ifindex for " << iface + << ": " << strerror(errno)); + return -ENOENT; + } + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.i)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = RTM_NEWLINK; + + req.i.ifi_family = AF_PACKET; + req.i.ifi_index = ifindex; + req.i.ifi_change |= IFF_UP; + if (up) + { + req.i.ifi_flags |= IFF_UP; + } + else + { + req.i.ifi_flags &= ~IFF_UP; + } + + OPENVPN_LOG(__func__ << ": set " << iface << " " << (up ? "up" : "down")); + + return sitnl_send(&req.n, 0, 0, NULL, NULL); + } + + static int + net_iface_mtu_set(std::string& iface, uint32_t mtu) + { + struct sitnl_link_req req = { }; + int ifindex; + + if (iface.empty()) + { + OPENVPN_LOG(__func__ << ": passed empty interface"); + return -EINVAL; + } + + ifindex = if_nametoindex(iface.c_str()); + if (ifindex == 0) + { + OPENVPN_LOG(__func__ << ": rtnl: cannot get ifindex for " << iface); + return -1; + } + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.i)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = RTM_NEWLINK; + + req.i.ifi_family = AF_PACKET; + req.i.ifi_index = ifindex; + + SITNL_ADDATTR(&req.n, sizeof(req), IFLA_MTU, &mtu, 4); + + OPENVPN_LOG(__func__ << ": mtu " << mtu << " for " << iface); + +err: + return sitnl_send(&req.n, 0, 0, NULL, NULL); + } + + static int + net_addr_add(const std::string& iface, const IPv4::Addr& addr, + const int prefixlen, const IPv4::Addr& broadcast) + { + OPENVPN_LOG(__func__ << ": " << addr << "/" << prefixlen << " brd " + << broadcast << " dev " << iface); + + return sitnl_addr_add(iface, IP::Addr::from_ipv4(addr), prefixlen, + IP::Addr::from_ipv4(broadcast)); + } + + static int + net_addr_add(const std::string& iface, const IPv6::Addr& addr, + const int prefixlen) + { + OPENVPN_LOG(__func__ << ": " << addr << "/" << prefixlen << " dev " << iface); + + return sitnl_addr_add(iface, IP::Addr::from_ipv6(addr), prefixlen, + IP::Addr::from_zero(IP::Addr::V6)); + } + + static int + net_addr_del(const std::string& iface, const IPv4::Addr& addr, + const int prefixlen) + { + OPENVPN_LOG(__func__ << ": " << addr << "/" << prefixlen << " dev " << iface); + + return sitnl_addr_del(iface, IP::Addr::from_ipv4(addr), prefixlen); + } + + static int + net_addr_del(const std::string& iface, const IPv6::Addr& addr, + const int prefixlen) + { + OPENVPN_LOG(__func__ << ": " << addr << "/" << prefixlen << " dev " << iface); + + return sitnl_addr_del(iface, IP::Addr::from_ipv6(addr), prefixlen); + } + + static int + net_addr_ptp_add(const std::string& iface, const IPv4::Addr& local, + const IPv4::Addr& remote) + { + OPENVPN_LOG(__func__ << ": " << local << " peer " << remote << " dev " << iface); + + return sitnl_addr_ptp_add(iface, IP::Addr::from_ipv4(local), + IP::Addr::from_ipv4(remote)); + } + + static int + net_addr_ptp_del(const std::string& iface, const IPv4::Addr& local, + const IPv4::Addr& remote) + { + OPENVPN_LOG(__func__ << ": " << local << " dev " << iface); + + return sitnl_addr_ptp_del(iface, IP::Addr::from_ipv4(local)); + } + + static int + net_route_add(const IP::Route4& route, const IPv4::Addr& gw, + const std::string& iface, const uint32_t table, + const int metric) + { + OPENVPN_LOG(__func__ << ": " << route << " via " << gw << " dev " << iface + << " table " << table << " metric " << metric); + + return sitnl_route_add(IP::Route(IP::Addr::from_ipv4(route.addr), route.prefix_len), + IP::Addr::from_ipv4(gw), iface, table, metric); + } + + static int + net_route_add(const IP::Route6& route, const IPv6::Addr& gw, + const std::string& iface, const uint32_t table, + const int metric) + { + OPENVPN_LOG(__func__ << ": " << route << " via " << gw << " dev " << iface + << " table " << table << " metric " << metric); + + return sitnl_route_add(IP::Route(IP::Addr::from_ipv6(route.addr), route.prefix_len), + IP::Addr::from_ipv6(gw), iface, table, metric); + } + + static int + net_route_del(const IP::Route4& route, const IPv4::Addr& gw, + const std::string& iface, const uint32_t table, + const int metric) + { + OPENVPN_LOG(__func__ << ": " << route << " via " << gw << " dev " << iface + << " table " << table << " metric " << metric); + + return sitnl_route_del(IP::Route(IP::Addr::from_ipv4(route.addr), route.prefix_len), + IP::Addr::from_ipv4(gw), iface, table, metric); + } + + static int + net_route_del(const IP::Route6& route, const IPv6::Addr& gw, + const std::string& iface, const uint32_t table, + const int metric) + { + OPENVPN_LOG(__func__ << ": " << route << " via " << gw << " dev " << iface + << " table " << table << " metric " << metric); + + return sitnl_route_del(IP::Route(IP::Addr::from_ipv6(route.addr), route.prefix_len), + IP::Addr::from_ipv6(gw), iface, table, metric); + } + }; + } +} diff --git a/openvpn/tun/linux/client/tuncli.hpp b/openvpn/tun/linux/client/tuncli.hpp index fccccdd..e6daa67 100644 --- a/openvpn/tun/linux/client/tuncli.hpp +++ b/openvpn/tun/linux/client/tuncli.hpp @@ -30,7 +30,15 @@ #include #include #include + +// check if Netlink has been selected at compile time +#ifdef OPENVPN_USE_SITNL +#include +#define TUN_LINUX TunNetlink +#else #include +#define TUN_LINUX TunLinux +#endif namespace openvpn { namespace TunLinux { @@ -114,7 +122,7 @@ namespace openvpn { if (tun_setup_factory) return tun_setup_factory->new_setup_obj(); else - return new TunLinux::Setup(); + return new TUN_LINUX::Setup(); } private: @@ -129,7 +137,7 @@ namespace openvpn { typedef Tun TunImpl; public: - virtual void tun_start(const OptionList& opt, TransportClient& transcli, CryptoDCSettings&) + virtual void tun_start(const OptionList& opt, TransportClient& transcli, CryptoDCSettings&) override { if (!impl) { @@ -184,7 +192,7 @@ namespace openvpn { tun_setup = config->new_setup_obj(); // create config object for tun setup layer - Setup::Config tsconf; + TUN_LINUX::Setup::Config tsconf; tsconf.layer = config->tun_prop.layer; tsconf.dev_name = config->dev_name; tsconf.txqueuelen = config->txqueuelen; @@ -228,12 +236,12 @@ namespace openvpn { } } - virtual bool tun_send(BufferAllocated& buf) + virtual bool tun_send(BufferAllocated& buf) override { return send(buf); } - virtual std::string tun_name() const + virtual std::string tun_name() const override { if (impl) return impl->name(); @@ -241,7 +249,7 @@ namespace openvpn { return "UNDEF_TUN"; } - virtual std::string vpn_ip4() const + virtual std::string vpn_ip4() const override { if (state->vpn_ip4_addr.specified()) return state->vpn_ip4_addr.to_string(); @@ -249,7 +257,7 @@ namespace openvpn { return ""; } - virtual std::string vpn_ip6() const + virtual std::string vpn_ip6() const override { if (state->vpn_ip6_addr.specified()) return state->vpn_ip6_addr.to_string(); @@ -273,11 +281,11 @@ namespace openvpn { return ""; } - virtual void set_disconnect() + virtual void set_disconnect() override { } - virtual void stop() { stop_(); } + virtual void stop() override { stop_(); } virtual ~Client() { stop_(); } private: diff --git a/openvpn/tun/linux/client/tunnetlink.hpp b/openvpn/tun/linux/client/tunnetlink.hpp new file mode 100644 index 0000000..17f0536 --- /dev/null +++ b/openvpn/tun/linux/client/tunnetlink.hpp @@ -0,0 +1,821 @@ +// OpenVPN -- An application to securely tunnel IP networks +// over a single port, with support for SSL/TLS-based +// session authentication and key exchange, +// packet encryption, packet authentication, and +// packet compression. +// +// Copyright (C) 2012-2018 OpenVPN Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License Version 3 +// 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program in the COPYING file. +// If not, see . + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace openvpn { + namespace TunNetlink { + + OPENVPN_EXCEPTION(tun_linux_error); + OPENVPN_EXCEPTION(tun_open_error); + OPENVPN_EXCEPTION(tun_layer_error); + OPENVPN_EXCEPTION(tun_ioctl_error); + OPENVPN_EXCEPTION(tun_fcntl_error); + OPENVPN_EXCEPTION(tun_name_error); + OPENVPN_EXCEPTION(tun_tx_queue_len_error); + OPENVPN_EXCEPTION(tun_ifconfig_error); + + struct NetlinkLinkSet : public Action + { + typedef RCPtr Ptr; + + NetlinkLinkSet() {} + + NetlinkLinkSet(std::string dev_arg, bool up_arg, int mtu_arg) + : dev(dev_arg), + up(up_arg), + mtu(mtu_arg) + { + } + + NetlinkLinkSet* copy() const + { + NetlinkLinkSet *ret = new NetlinkLinkSet; + ret->dev = dev; + ret->up = up; + ret->mtu = mtu; + return ret; + } + + virtual void execute(std::ostream& os) override + { + int ret; + + if (dev.empty()) + { + os << "Error: can't call NetlinkLinkSet with no interface" << std::endl; + return; + } + + ret = SITNL::net_iface_mtu_set(dev, mtu); + if (ret) + { + os << "Error while executing NetlinkLinkSet " << dev << " mtu " << mtu + << ": " << ret << std::endl; + } + + ret = SITNL::net_iface_up(dev, up); + if (ret) + { + os << "Error while executing NetlinkLinkSet " << dev << " up " << up + << ": " << ret << std::endl; + } + } + + virtual std::string to_string() const override + { + std::ostringstream os; + os << "netlink iface " << dev << " link set " << up << " mtu " << mtu; + return os.str(); + } + + std::string dev; + bool up; + int mtu; + }; + + struct NetlinkAddr4 : public Action + { + typedef RCPtr Ptr; + + NetlinkAddr4() {} + + NetlinkAddr4(std::string dev_arg, IPv4::Addr& addr_arg, int prefixlen_arg, + IPv4::Addr& broadcast_arg, bool add_arg) + : dev(dev_arg), + addr(addr_arg), + prefixlen(prefixlen_arg), + broadcast(broadcast_arg), + add(add_arg) + { + } + + NetlinkAddr4* copy() const + { + NetlinkAddr4 *ret = new NetlinkAddr4; + ret->dev = dev; + ret->addr = addr; + ret->prefixlen = prefixlen; + ret->broadcast = broadcast; + ret->add = add; + return ret; + } + + virtual void execute(std::ostream& os) override + { + int ret; + + if (dev.empty()) + { + os << "Error: can't call NetlinkAddr4 with no interface" << std::endl; + return; + } + + if (add) + { + ret = SITNL::net_addr_add(dev, addr, prefixlen, broadcast); + } + else + { + ret = SITNL::net_addr_del(dev, addr, prefixlen); + } + + if (ret) + { + os << "Error while executing NetlinkAddr4(add: " << add << ") " + << dev << ": " << ret << std::endl; + } + } + + virtual std::string to_string() const override + { + std::ostringstream os; + os << "netlink iface " << dev << " " << (add ? "add" : "del") << " " + << addr.to_string() << "/" << prefixlen << " broadcast " + << broadcast.to_string(); + return os.str(); + } + + std::string dev; + IPv4::Addr addr; + int prefixlen; + IPv4::Addr broadcast; + bool add; + }; + + struct NetlinkAddr6 : public Action + { + typedef RCPtr Ptr; + + NetlinkAddr6() {} + + NetlinkAddr6(std::string dev_arg, IPv6::Addr& addr_arg, int prefixlen_arg, + bool add_arg) + : dev(dev_arg), + addr(addr_arg), + prefixlen(prefixlen_arg), + add(add_arg) + { + } + + NetlinkAddr6* copy() const + { + NetlinkAddr6 *ret = new NetlinkAddr6; + ret->dev = dev; + ret->addr = addr; + ret->prefixlen = prefixlen; + ret->add = add; + return ret; + } + + virtual void execute(std::ostream& os) override + { + int ret; + + if (dev.empty()) + { + os << "Error: can't call NetlinkAddr6 with no interface" << std::endl; + return; + } + + if (add) + { + ret = SITNL::net_addr_add(dev, addr, prefixlen); + } + else + { + ret = SITNL::net_addr_del(dev, addr, prefixlen); + } + + if (ret) + { + os << "Error while executing NetlinkAddr6(add: " << add << ") " + << dev << ": " << ret << std::endl; + } + } + + virtual std::string to_string() const override + { + std::ostringstream os; + os << "netlink iface " << dev << " " << (add ? "add" : "del") << " " + << addr.to_string() << "/" << prefixlen; + return os.str(); + } + + std::string dev; + IPv6::Addr addr; + int prefixlen; + bool add; + }; + + struct NetlinkAddr4PtP : public Action + { + typedef RCPtr Ptr; + + NetlinkAddr4PtP() {} + + NetlinkAddr4PtP(std::string dev_arg, IPv4::Addr local_arg, + IPv4::Addr remote_arg, bool add_arg) + : dev(dev_arg), + local(local_arg), + remote(remote_arg), + add(add_arg) + { + } + + NetlinkAddr4PtP* copy() const + { + NetlinkAddr4PtP *ret = new NetlinkAddr4PtP; + ret->dev = dev; + ret->local = local; + ret->remote = remote; + ret->add = add; + return ret; + } + + virtual void execute(std::ostream& os) override + { + int ret; + + if (dev.empty()) + { + os << "Error: can't call NetlinkAddr4PtP with no interface" << std::endl; + return; + } + + if (add) + { + ret = SITNL::net_addr_ptp_add(dev, local, remote); + } + else + { + ret = SITNL::net_addr_ptp_del(dev, local, remote); + } + + if (ret) + { + os << "Error while executing NetlinkAddr4PtP(add: " << add << ") " + << dev << ": " << ret << std::endl; + } + } + + virtual std::string to_string() const override + { + return "netlink iface " + dev + " " + (add ? "add" : "del") + " ptp " + + local.to_string() + " remote " + remote.to_string(); + } + + std::string dev; + IPv4::Addr local; + IPv4::Addr remote; + bool add; + }; + + struct NetlinkRoute4 : public Action + { + typedef RCPtr Ptr; + + NetlinkRoute4() {} + + NetlinkRoute4(IPv4::Addr& dst_arg, int prefixlen_arg, IPv4::Addr& gw_arg, + std::string dev_arg, bool add_arg) + : route(dst_arg, prefixlen_arg), + gw(gw_arg), + dev(dev_arg), + add(add_arg) + { + } + + NetlinkRoute4* copy() const + { + NetlinkRoute4 *ret = new NetlinkRoute4; + ret->route = route; + ret->gw = gw; + ret->dev = dev; + return ret; + } + + virtual void execute(std::ostream& os) override + { + int ret; + + if (dev.empty()) + { + os << "Error: can't call NetlinkRoute4 with no interface" << std::endl; + return; + } + + if (add) + { + ret = SITNL::net_route_add(route, gw, dev, 0, 0); + } + else + { + ret = SITNL::net_route_del(route, gw, dev, 0, 0); + } + + if (ret) + { + os << "Error while executing NetlinkRoute4(add: " << add << ") " + << dev << ": " << ret << std::endl; + } + } + + virtual std::string to_string() const override + { + std::ostringstream os; + os << "netlink route " << (add ? "add" : "del") << " dev " << dev << " " + << route << " via " << gw.to_string(); + return os.str(); + } + + IP::Route4 route; + IPv4::Addr gw; + std::string dev; + bool add; + }; + + struct NetlinkRoute6 : public Action + { + typedef RCPtr Ptr; + + NetlinkRoute6() {} + + NetlinkRoute6(IPv6::Addr& dst_arg, int prefixlen_arg, IPv6::Addr& gw_arg, + std::string dev_arg, bool add_arg) + : route(dst_arg, prefixlen_arg), + gw(gw_arg), + dev(dev_arg), + add(add_arg) + { + } + + NetlinkRoute6* copy() const + { + NetlinkRoute6 *ret = new NetlinkRoute6; + ret->route = route; + ret->gw = gw; + ret->dev = dev; + return ret; + } + + virtual void execute(std::ostream& os) override + { + int ret; + + if (dev.empty()) + { + os << "Error: can't call NetlinkRoute6 with no interface" << std::endl; + return; + } + + if (add) + { + ret = SITNL::net_route_add(route, gw, dev, 0, 0); + } + else + { + ret = SITNL::net_route_del(route, gw, dev, 0, 0); + } + + if (ret) + { + os << "Error while executing NetlinkRoute6(add: " << add << ") " + << dev << ": " << ret << std::endl; + } + } + + virtual std::string to_string() const override + { + std::ostringstream os; + os << "netlink route " << (add ? "add" : "del") << " dev " << dev << " " + << route << " via " << gw.to_string(); + return os.str(); + } + + IP::Route6 route; + IPv6::Addr gw; + std::string dev; + bool add; + }; + + enum { // add_del_route flags + R_IPv6=(1<<0), + R_ADD_SYS=(1<<1), + R_ADD_DCO=(1<<2), + R_ADD_ALL=R_ADD_SYS|R_ADD_DCO, + }; + + /*inline IPv4::Addr cvt_pnr_ip_v4(const std::string& hexaddr) + { + BufferAllocated v(4, BufferAllocated::CONSTRUCT_ZERO); + parse_hex(v, hexaddr); + if (v.size() != 4) + throw tun_linux_error("bad hex address"); + IPv4::Addr ret = IPv4::Addr::from_bytes(v.data()); + return IP::Addr::from_ipv4(ret); + }*/ + + inline void add_del_route(const std::string& addr_str, + const int prefix_len, + const std::string& gateway_str, + const std::string& dev, + const unsigned int flags, + std::vector* rtvec, + Action::Ptr& create, + Action::Ptr& destroy) + { + if (flags & R_IPv6) + { + const IPv6::Addr addr = IPv6::Addr::from_string(addr_str); + const IPv6::Addr netmask = IPv6::Addr::netmask_from_prefix_len(prefix_len); + const IPv6::Addr net = addr & netmask; + + if (flags & R_ADD_SYS) + { + // ip route add 2001:db8:1::/48 via 2001:db8:1::1 + NetlinkRoute6::Ptr add(new NetlinkRoute6); + add->route.addr = net; + add->route.prefix_len = prefix_len; + add->gw = IPv6::Addr::from_string(gateway_str); + add->dev = dev; + add->add = true; + + create = add; + // for the destroy command, copy the add command but replace "add" with "delete" + NetlinkRoute6::Ptr del(add->copy()); + del->add = false; + destroy = del; + } + + if (rtvec && (flags & R_ADD_DCO)) + rtvec->emplace_back(IP::Addr::from_ipv6(net), prefix_len); + } + else + { + const IPv4::Addr addr = IPv4::Addr::from_string(addr_str); + const IPv4::Addr netmask = IPv4::Addr::netmask_from_prefix_len(prefix_len); + const IPv4::Addr net = addr & netmask; + + if (flags & R_ADD_SYS) + { + // ip route add 192.0.2.128/25 via 192.0.2.1 + NetlinkRoute4::Ptr add(new NetlinkRoute4); + add->route.addr = net; + add->route.prefix_len = prefix_len; + add->gw = IPv4::Addr::from_string(gateway_str); + add->dev = dev; + add->add = true; + + create = add; + // for the destroy command, copy the add command but replace "add" with "delete" + NetlinkRoute4::Ptr del(add->copy()); + del->add = false; + destroy = del; + } + + if (rtvec && (flags & R_ADD_DCO)) + rtvec->emplace_back(IP::Addr::from_ipv4(net), prefix_len); + } + } + + inline void add_del_route(const std::string& addr_str, + const int prefix_len, + const std::string& gateway_str, + const std::string& dev, + const unsigned int flags,// add interface route to rtvec if defined + std::vector* rtvec, + ActionList& create, + ActionList& destroy) + { + Action::Ptr c, d; + add_del_route(addr_str, prefix_len, gateway_str, dev, flags, rtvec, c, d); + create.add(c); + destroy.add(d); + } + + inline void iface_up(const std::string& iface_name, + const int mtu, + ActionList& create, + ActionList& destroy) + { + { + NetlinkLinkSet::Ptr add(new NetlinkLinkSet); + add->dev = iface_name; + add->up = true; + add->mtu = mtu; + + create.add(add); + // for the destroy command, copy the add command but replace "up" with "down" + NetlinkLinkSet::Ptr del(add->copy()); + del->up = false; + destroy.add(del); + } + } + + inline void iface_config(const std::string& iface_name, + int unit, + const TunBuilderCapture& pull, + std::vector* rtvec, + ActionList& create, + ActionList& destroy) + { + // set local4 and local6 to point to IPv4/6 route configurations + const TunBuilderCapture::RouteAddress* local4 = pull.vpn_ipv4(); + const TunBuilderCapture::RouteAddress* local6 = pull.vpn_ipv6(); + + // Set IPv4 Interface + if (local4) + { + NetlinkAddr4::Ptr add(new NetlinkAddr4); + add->addr = IPv4::Addr::from_string(local4->address); + add->prefixlen = local4->prefix_length; + add->broadcast = IPv4::Addr::from_string(local4->address) + | ~IPv4::Addr::netmask_from_prefix_len(local4->prefix_length); + add->dev = iface_name; + add->add = true; +// if (unit >= 0) +// { +// add->argv.push_back("label"); +// add->argv.push_back(iface_name + ':' + openvpn::to_string(unit)); +// } + create.add(add); + + // for the destroy command, copy the add command but replace "add" with "delete" + NetlinkAddr4::Ptr del(add->copy()); + del->add = false; + destroy.add(del); + + // add interface route to rtvec if defined + add_del_route(local4->address, local4->prefix_length, local4->address, iface_name, R_ADD_DCO, rtvec, create, destroy); + } + + // Set IPv6 Interface + if (local6 && !pull.block_ipv6) + { + NetlinkAddr6::Ptr add(new NetlinkAddr6); + add->addr = IPv6::Addr::from_string(local6->address); + add->prefixlen = local6->prefix_length; + add->dev = iface_name; + add->add = true; + + create.add(add); + + // for the destroy command, copy the add command but replace "add" with "delete" + NetlinkAddr6::Ptr del(add->copy()); + del->add = false; + destroy.add(del); + + // add interface route to rtvec if defined + add_del_route(local6->address, local6->prefix_length, local6->address, iface_name, R_ADD_DCO|R_IPv6, rtvec, create, destroy); + } + } + + inline void tun_config(const std::string& iface_name, + const TunBuilderCapture& pull, + std::vector* rtvec, + ActionList& create, + ActionList& destroy) + { + const LinuxGW46Netlink gw; + + // set local4 and local6 to point to IPv4/6 route configurations + const TunBuilderCapture::RouteAddress* local4 = pull.vpn_ipv4(); + const TunBuilderCapture::RouteAddress* local6 = pull.vpn_ipv6(); + + // configure interface + iface_up(iface_name, pull.mtu, create, destroy); + iface_config(iface_name, -1, pull, rtvec, create, destroy); + + // Process Routes + { + for (const auto &route : pull.add_routes) + { + if (route.ipv6) + { + if (!pull.block_ipv6) + add_del_route(route.address, route.prefix_length, local6->gateway, iface_name, R_ADD_ALL|R_IPv6, rtvec, create, destroy); + } + else + { + if (local4 && !local4->gateway.empty()) + add_del_route(route.address, route.prefix_length, local4->gateway, iface_name, R_ADD_ALL, rtvec, create, destroy); + else + OPENVPN_LOG("ERROR: IPv4 route pushed without IPv4 ifconfig and/or route-gateway"); + } + } + } + + // Process exclude routes + { + for (const auto &route : pull.exclude_routes) + { + if (route.ipv6) + { + OPENVPN_LOG("NOTE: exclude IPv6 routes not supported yet"); // fixme + } + else + { + if (gw.v4.defined()) + add_del_route(route.address, route.prefix_length, gw.v4.addr().to_string(), gw.v4.dev(), R_ADD_SYS, rtvec, create, destroy); + else + OPENVPN_LOG("NOTE: cannot determine gateway for exclude IPv4 routes"); + } + } + } + + // Process IPv4 redirect-gateway + if (pull.reroute_gw.ipv4) + { + // add bypass route + if (!pull.remote_address.ipv6 && !(pull.reroute_gw.flags & RedirectGatewayFlags::RG_LOCAL)) + add_del_route(pull.remote_address.address, 32, gw.v4.addr().to_string(), gw.v4.dev(), R_ADD_SYS, rtvec, create, destroy); + + add_del_route("0.0.0.0", 1, local4->gateway, iface_name, R_ADD_ALL, rtvec, create, destroy); + add_del_route("128.0.0.0", 1, local4->gateway, iface_name, R_ADD_ALL, rtvec, create, destroy); + } + + // Process IPv6 redirect-gateway + if (pull.reroute_gw.ipv6 && !pull.block_ipv6) + { + // add bypass route + if (pull.remote_address.ipv6 && !(pull.reroute_gw.flags & RedirectGatewayFlags::RG_LOCAL)) + add_del_route(pull.remote_address.address, 128, gw.v6.addr().to_string(), gw.v6.dev(), R_ADD_SYS|R_IPv6, rtvec, create, destroy); + + add_del_route("0000::", 1, local6->gateway, iface_name, R_ADD_ALL|R_IPv6, rtvec, create, destroy); + add_del_route("8000::", 1, local6->gateway, iface_name, R_ADD_ALL|R_IPv6, rtvec, create, destroy); + } + + // fixme -- Process block-ipv6 + + // fixme -- Handle pushed DNS servers + } + + class Setup : public TunBuilderSetup::Base + { + public: + typedef RCPtr Ptr; + + struct Config : public TunBuilderSetup::Config + { + std::string iface_name; + Layer layer; // OSI layer + std::string dev_name; + int txqueuelen; + +#ifdef HAVE_JSON + virtual Json::Value to_json() override + { + Json::Value root(Json::objectValue); + root["iface_name"] = Json::Value(iface_name); + root["layer"] = Json::Value(layer.str()); + root["dev_name"] = Json::Value(dev_name); + root["txqueuelen"] = Json::Value(txqueuelen); + return root; + }; + + virtual void from_json(const Json::Value& root, const std::string& title) override + { + json::assert_dict(root, title); + json::to_string(root, iface_name, "iface_name", title); + layer = Layer::from_str(json::get_string(root, "layer", title)); + json::to_string(root, dev_name, "dev_name", title); + json::to_int(root, txqueuelen, "txqueuelen", title); + } +#endif + }; + + virtual void destroy(std::ostream &os) override + { + // remove added routes + if (remove_cmds) + remove_cmds->execute(std::cout); + } + + virtual int establish(const TunBuilderCapture& pull, // defined by TunBuilderSetup::Base + TunBuilderSetup::Config* config, + Stop* stop, + std::ostream& os) override + { + // get configuration + Config *conf = dynamic_cast(config); + if (!conf) + throw tun_linux_error("missing config"); + + static const char node[] = "/dev/net/tun"; + ScopedFD fd(open(node, O_RDWR)); + if (!fd.defined()) + OPENVPN_THROW(tun_open_error, "error opening tun device " << node << ": " << errinfo(errno)); + + struct ifreq ifr; + std::memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags = IFF_ONE_QUEUE; + ifr.ifr_flags |= IFF_NO_PI; + if (conf->layer() == Layer::OSI_LAYER_3) + ifr.ifr_flags |= IFF_TUN; + else if (conf->layer() == Layer::OSI_LAYER_2) + ifr.ifr_flags |= IFF_TAP; + else + throw tun_layer_error("unknown OSI layer"); + + open_unit(conf->dev_name, ifr, fd); + + if (fcntl (fd(), F_SETFL, O_NONBLOCK) < 0) + throw tun_fcntl_error(errinfo(errno)); + + // Set the TX send queue size + if (conf->txqueuelen) + { + struct ifreq netifr; + ScopedFD ctl_fd(socket (AF_INET, SOCK_DGRAM, 0)); + + if (ctl_fd.defined()) + { + std::memset(&netifr, 0, sizeof(netifr)); + strcpy (netifr.ifr_name, ifr.ifr_name); + netifr.ifr_qlen = conf->txqueuelen; + if (ioctl (ctl_fd(), SIOCSIFTXQLEN, (void *) &netifr) < 0) + throw tun_tx_queue_len_error(errinfo(errno)); + } + else + throw tun_tx_queue_len_error(errinfo(errno)); + } + + conf->iface_name = ifr.ifr_name; + + ActionList::Ptr add_cmds = new ActionList(); + remove_cmds.reset(new ActionListReversed()); // remove commands executed in reversed order + + // configure tun properties + tun_config(ifr.ifr_name, pull, nullptr, *add_cmds, *remove_cmds); + + // execute commands to bring up interface + add_cmds->execute(std::cout); + + return fd.release(); + } + + private: + void open_unit(const std::string& name, struct ifreq& ifr, ScopedFD& fd) + { + if (!name.empty()) + { + const int max_units = 256; + for (int unit = 0; unit < max_units; ++unit) + { + std::string n = name; + if (unit) + n += openvpn::to_string(unit); + if (n.length() < IFNAMSIZ) + ::strcpy (ifr.ifr_name, n.c_str()); + else + throw tun_name_error(); + if (ioctl (fd(), TUNSETIFF, (void *) &ifr) == 0) + return; + } + const int eno = errno; + OPENVPN_THROW(tun_ioctl_error, "failed to open tun device '" << name << "' after trying " << max_units << " units : " << errinfo(eno)); + } + else + { + if (ioctl (fd(), TUNSETIFF, (void *) &ifr) < 0) + { + const int eno = errno; + OPENVPN_THROW(tun_ioctl_error, "failed to open tun device '" << name << "' : " << errinfo(eno)); + } + } + } + + ActionListReversed::Ptr remove_cmds; + }; + } +} // namespace openvpn diff --git a/openvpn/tun/linux/client/tunsetup.hpp b/openvpn/tun/linux/client/tunsetup.hpp index b9accc0..0d9eadb 100644 --- a/openvpn/tun/linux/client/tunsetup.hpp +++ b/openvpn/tun/linux/client/tunsetup.hpp @@ -364,7 +364,7 @@ namespace openvpn { #endif }; - virtual void destroy(std::ostream &os) + virtual void destroy(std::ostream &os) override { // remove added routes if (remove_cmds) diff --git a/scripts/android/build-boost b/scripts/android/build-boost index eaa89e4..e1536b8 100755 --- a/scripts/android/build-boost +++ b/scripts/android/build-boost @@ -17,7 +17,7 @@ cd $DEP_DIR rm -rf boost mkdir boost export LINK_MODE=static -export TARGETS="android-a8a android-a8a-dbg android-a7a android-a7a-dbg android android-dbg" +export TARGETS="android-a8a android-a8a-dbg android-a7a android-a7a-dbg" export SDK_PATH_SCRIPT=$O3/core/vars/android-sdk-path $O3/core/deps/boost/build-boost exit 0 diff --git a/scripts/android/build-lzo b/scripts/android/build-lzo index 7fedc43..2d39e2e 100755 --- a/scripts/android/build-lzo +++ b/scripts/android/build-lzo @@ -15,7 +15,7 @@ cd $DEP_DIR rm -rf lzo mkdir lzo -for target in android-a8a android-a8a-dbg android-a7a android-a7a-dbg android android-dbg ; do +for target in android-a8a android-a8a-dbg android-a7a android-a7a-dbg ; do echo '***************' TARGET $target TARGET=$target $O3/core/deps/lzo/build-lzo done diff --git a/scripts/android/build-minicrypto b/scripts/android/build-minicrypto index a2e954f..3f613fd 100755 --- a/scripts/android/build-minicrypto +++ b/scripts/android/build-minicrypto @@ -15,7 +15,7 @@ cd $DEP_DIR rm -rf minicrypto mkdir minicrypto -for target in android android-dbg android-a7a android-a7a-dbg ; do +for target in android-a7a android-a7a-dbg ; do echo '***************' TARGET $target TARGET=$target $O3/core/deps/minicrypto/build-minicrypto done diff --git a/scripts/android/build-polarssl b/scripts/android/build-polarssl index e04e51c..9950c9a 100755 --- a/scripts/android/build-polarssl +++ b/scripts/android/build-polarssl @@ -18,7 +18,7 @@ mini=0 rm -rf polarssl mkdir polarssl -for target in android-a8a android-a8a-dbg android-a7a android-a7a-dbg android android-dbg ; do +for target in android-a8a android-a8a-dbg android-a7a android-a7a-dbg ; do echo '***************' TARGET $target VERBOSE=1 TARGET=$target CMAKE_TARGET=android USE_MINICRYPTO=$mini MINICRYPTO_DIR=$(pwd)/minicrypto/minicrypto-$target $O3/core/deps/polarssl/build-polarssl mv polarssl-$target polarssl/ diff --git a/scripts/android/build-sdk b/scripts/android/build-sdk index f8a8b95..01f1a49 100755 --- a/scripts/android/build-sdk +++ b/scripts/android/build-sdk @@ -15,31 +15,28 @@ fi if [ -d "$SDK" ]; then echo "Android SDK already exists at $SDK. Doing only update" - yes | $SDK/tools/bin/sdkmanager --licenses - $SDK/tools/bin/sdkmanager --update - exit 0 +else + . $O3/core/deps/functions.sh + + FNAME=sdk-tools-linux-3859397.zip + URL=https://dl.google.com/android/repository/${FNAME} + CSUM=444e22ce8ca0f67353bda4b85175ed3731cae3ffa695ca18119cbacef1c1bea0 + + download + + cd $DEP_DIR + rm -rf android-sdk + mkdir android-sdk + + . $O3/core/vars/android-sdk-path + + cd $SDK + unzip $DL/$FNAME fi -. $O3/core/deps/functions.sh - -FNAME=sdk-tools-linux-3859397.zip -URL=https://dl.google.com/android/repository/${FNAME} -CSUM=444e22ce8ca0f67353bda4b85175ed3731cae3ffa695ca18119cbacef1c1bea0 - -download - -cd $DEP_DIR -rm -rf android-sdk -mkdir android-sdk - -. $O3/core/vars/android-sdk-path - -cd $SDK -unzip $DL/$FNAME - yes | $SDK/tools/bin/sdkmanager --licenses $SDK/tools/bin/sdkmanager --update -$SDK/tools/bin/sdkmanager 'build-tools;26.0.2' \ +$SDK/tools/bin/sdkmanager --install 'build-tools;26.0.2' \ 'ndk-bundle' \ 'extras;android;m2repository' \ 'patcher;v4' \ diff --git a/scripts/android/build-snappy b/scripts/android/build-snappy index a94e439..394a1e8 100755 --- a/scripts/android/build-snappy +++ b/scripts/android/build-snappy @@ -15,7 +15,7 @@ cd $DEP_DIR rm -rf snappy mkdir snappy -for target in android-a8a android-a8a-dbg android-a7a android-a7a-dbg android android-dbg ; do +for target in android-a8a android-a8a-dbg android-a7a android-a7a-dbg ; do echo '***************' TARGET $target TARGET=$target $O3/core/deps/snappy/build-snappy done diff --git a/scripts/android/build-toolchain b/scripts/android/build-toolchain index efe9b6f..0f8c613 100755 --- a/scripts/android/build-toolchain +++ b/scripts/android/build-toolchain @@ -39,7 +39,7 @@ $NDK/build/tools/make-standalone-toolchain.sh \ --toolchain=$ABI-$ABI_VER \ --stl=gnustl \ --arch=arm \ - --platform=android-14 \ + --platform=android-16 \ --install-dir=$DEST cd $DEST/$ABI/bin ln -s ../../bin/$ABI-gcc cc @@ -58,7 +58,7 @@ $NDK/build/tools/make-standalone-toolchain.sh \ --toolchain=$ABI-$ABI_VER \ --stl=gnustl \ --arch=x86 \ - --platform=android-14 \ + --platform=android-16 \ --install-dir=$DEST cd $DEST/$SUB/bin ln -s ../../bin/$SUB-gcc cc diff --git a/scripts/build b/scripts/build index 8f8ee88..cd3f872 100755 --- a/scripts/build +++ b/scripts/build @@ -242,8 +242,12 @@ fi # Cityhash if [ "$CITY" = "1" ]; then - LIBDIRS="$LIBDIRS -L$DEP_DIR/cityhash/cityhash-$PLATFORM/lib" - CPPFLAGS="$CPPFLAGS -I$DEP_DIR/cityhash/cityhash-$PLATFORM/include" + if [ -d "$DEP_DIR/cityhash/cityhash-$PLATFORM/lib" ]; then + LIBDIRS="$LIBDIRS -L$DEP_DIR/cityhash/cityhash-$PLATFORM/lib" + fi + if [ -d "$DEP_DIR/cityhash/cityhash-$PLATFORM/include" ]; then + CPPFLAGS="$CPPFLAGS -I$DEP_DIR/cityhash/cityhash-$PLATFORM/include" + fi LIBS="$LIBS -lcityhash" CPPFLAGS="$CPPFLAGS -DHAVE_CITYHASH" fi diff --git a/scripts/update-copyright b/scripts/update-copyright new file mode 100755 index 0000000..d831fcf --- /dev/null +++ b/scripts/update-copyright @@ -0,0 +1,51 @@ +#!/bin/sh +# update-copyright - Simple tool to update the Copyright lines +# in all files checked into git +# +# Copyright (C) 2018 OpenVPN Inc +# Copyright (C) 2018 David Sommerseth +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License Version 3 +# 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program in the COPYING file. +# If not, see . +# + +# Basic shell sanity +set -eu + +# Simple argument control +if [ $# -ne 1 ]; then + echo "Usage: $0 " + exit 1 +fi + +# Only update Copyright lines with these owners +# The 'or' operator is GNU sed specific, and must be \| +UPDATE_COPYRIGHT_LINES="OpenVPN Inc\|@openvpn\.net\\|unstable\.cc\|gmail\.com\|@fox-it\.com" +COPY_YEAR="$1" + +cd "$(git rev-parse --show-toplevel)" +for file in $(git ls-files | grep -v deps/); +do + echo -n "Updating $file ..." + # The first sed operation covers 20xx-20yy copyright lines, + # The second sed operation changes 20xx -> 20xx-20yy + sed -e "/$UPDATE_COPYRIGHT_LINES/s/\(Copyright (C) 20..-\)\(20..\)[[:blank:]]\+/\1$COPY_YEAR /" \ + -e "/$UPDATE_COPYRIGHT_LINES/s/\(Copyright (C) \)\(20..\)[[:blank:]]\+/\1\2-$COPY_YEAR /" \ + -e "/$UPDATE_COPYRIGHT_LINES/s/\(Copyright (C) $COPY_YEAR\)\(-$COPY_YEAR\)[[:blank:]]\+/\1 /" \ + -i $file + echo " Done" +done +echo +echo "** All files updated with $COPY_YEAR as the ending copyright year" +echo +exit 0 diff --git a/test/ovpncli/cli.cpp b/test/ovpncli/cli.cpp index 717066c..ea08f63 100644 --- a/test/ovpncli/cli.cpp +++ b/test/ovpncli/cli.cpp @@ -80,6 +80,10 @@ #include #endif +#ifdef USE_NETCFG +#include "client/core-client-netcfg.hpp" +#endif + using namespace openvpn; namespace { @@ -141,9 +145,10 @@ public: #endif private: - virtual bool socket_protect(int socket) override + bool socket_protect(int socket, std::string remote, bool ipv6) override { - std::cout << "*** socket_protect " << socket << std::endl; + std::cout << "*** socket_protect " << socket << " " + << remote << std::endl; return true; } @@ -910,7 +915,13 @@ int openvpn_client(int argc, char *argv[], const std::string* profile_content) } else { +#if defined(USE_NETCFG) + DBus conn(G_BUS_TYPE_SYSTEM); + conn.Connect(); + NetCfgTunBuilder client(conn.GetConnection()); +#else Client client; +#endif const ClientAPI::EvalConfig eval = client.eval_config(config); if (eval.error) OPENVPN_THROW_EXCEPTION("eval config error: " << eval.message); diff --git a/test/ovpncli/go b/test/ovpncli/go index 8f1a1ec..873cf1f 100755 --- a/test/ovpncli/go +++ b/test/ovpncli/go @@ -15,6 +15,7 @@ GCC_EXTRA="$GCC_EXTRA -DOPENVPN_SHOW_SESSION_TOKEN" [ "$BS64" = "1" ] && GCC_EXTRA="$GCC_EXTRA -DOPENVPN_BS64_DATA_LIMIT=2500000" [ "$ROVER" = "1" ] && GCC_EXTRA="$GCC_EXTRA -DOPENVPN_REMOTE_OVERRIDE" [ "$TLS" = "1" ] && GCC_EXTRA="$GCC_EXTRA -DOPENVPN_TLS_LINK" +[ "$SITNL" = "1" ] && GCC_EXTRA="$GCC_EXTRA -DOPENVPN_USE_SITNL" if [ "$AGENT" = "1" ]; then GCC_EXTRA="$GCC_EXTRA -DOPENVPN_COMMAND_AGENT" fi @@ -23,9 +24,9 @@ export GCC_EXTRA # determine platform if [ "$(uname)" == "Darwin" ]; then - export PROF=osx64 + export PROF=${PROF:-osx64} elif [ "$(uname)" == "Linux" ]; then - export PROF=linux + export PROF=${PROF:-linux} else echo this script only knows how to build on Mac OS or Linux fi diff --git a/test/ssl/proto.cpp b/test/ssl/proto.cpp index 497abb3..c712aef 100644 --- a/test/ssl/proto.cpp +++ b/test/ssl/proto.cpp @@ -41,7 +41,8 @@ #if !defined(USE_TLS_AUTH) && !defined(USE_TLS_CRYPT) //#define USE_TLS_AUTH -#define USE_TLS_CRYPT +//#define USE_TLS_CRYPT +#define USE_TLS_CRYPT_V2 #endif #define OPENVPN_INSTRUMENTATION @@ -834,6 +835,8 @@ int test(const int thread_num) const std::string server_key = read_text("server.key"); const std::string dh_pem = read_text("dh.pem"); const std::string tls_auth_key = read_text("tls-auth.key"); + const std::string tls_crypt_v2_server_key = read_text("tls-crypt-v2-server.key"); + const std::string tls_crypt_v2_client_key = read_text("tls-crypt-v2-client.key"); // client config ClientSSLAPI::Config::Ptr cc(new ClientSSLAPI::Config()); @@ -888,6 +891,17 @@ int test(const int thread_num) cp->tls_crypt_factory.reset(new CryptoTLSCryptFactory()); cp->tls_key.parse(tls_auth_key); cp->set_tls_crypt_algs(CryptoAlgs::lookup("SHA256"), CryptoAlgs::lookup("AES-256-CTR")); +#endif +#ifdef USE_TLS_CRYPT_V2 + cp->tls_crypt_factory.reset(new CryptoTLSCryptFactory()); + cp->set_tls_crypt_algs(CryptoAlgs::lookup("SHA256"), CryptoAlgs::lookup("AES-256-CTR")); + { + TLSCryptV2ClientKey tls_crypt_v2_key(cp->tls_crypt_context); + tls_crypt_v2_key.parse(tls_crypt_v2_client_key); + tls_crypt_v2_key.extract_key(cp->tls_key); + tls_crypt_v2_key.extract_wkc(cp->wkc); + } + cp->tls_crypt_v2 = true; #endif cp->reliable_window = 4; cp->max_ack_list = 4; @@ -959,10 +973,21 @@ int test(const int thread_num) sp->set_tls_auth_digest(CryptoAlgs::lookup(PROTO_DIGEST)); sp->key_direction = 1; #endif -#ifdef USE_TLS_CRYPT - sp->tls_crypt_factory.reset(new CryptoTLSCryptFactory()); +#if defined(USE_TLS_CRYPT) + sp->tls_crypt_factory.reset(new CryptoTLSCryptFactory()); sp->tls_key.parse(tls_auth_key); sp->set_tls_crypt_algs(CryptoAlgs::lookup("SHA256"), CryptoAlgs::lookup("AES-256-CTR")); +#endif +#ifdef USE_TLS_CRYPT_V2 + sp->tls_crypt_factory.reset(new CryptoTLSCryptFactory()); + { + TLSCryptV2ServerKey tls_crypt_v2_key; + tls_crypt_v2_key.parse(tls_crypt_v2_server_key); + tls_crypt_v2_key.extract_key(sp->tls_key); + } + sp->set_tls_crypt_algs(CryptoAlgs::lookup("SHA256"), CryptoAlgs::lookup("AES-256-CTR")); + sp->tls_crypt_metadata_factory.reset(new CryptoTLSCryptMetadataFactory()); + sp->tls_crypt_v2 = true; #endif sp->reliable_window = 4; sp->max_ack_list = 4; diff --git a/test/ssl/tls-crypt-v2-client.key b/test/ssl/tls-crypt-v2-client.key new file mode 100644 index 0000000..ea6f933 --- /dev/null +++ b/test/ssl/tls-crypt-v2-client.key @@ -0,0 +1,14 @@ +-----BEGIN OpenVPN tls-crypt-v2 client key----- +fxgWlYpYT1H8ZCW3/139Ip6WSAa2QQZUu64N/x3g7RzhYuaInPRpP9GLpeDpj/PP +MOa0LuvbIr9Wm6V4+WUO+R8hOdfIMwtBlRleMDedFNRD5h1DCpoDdpgr5CxaPyqO +H6eCvNP7POmqdGJyo9L6H9ndyetQp4r/8wXeYWSkcJKsIJsmyBqRzuODTENGYCqG +FN3+XcUrEMYUyvb/c8NqDVQ6xpwc9+6N7840encSMZPYCq6o8J1QXZJ8sMoBEPa1 +gVtLCsPXp2oTs1h82NBpxO8BNPNFz1xIG+2Zy1NpU8PONnr0rtCRPNU7ejsG+p2I +vRDt3VOf+3aD2eVFesR4NAWYTZlxg3eLG4zD/xSiGblBltbB/7qGPxgK6WOP2XD/ +V0Yh+TruX2vo4xjEXqV1umTx9K8u6nvaC8uv/72NsvPWkuAOEpY6qpDrqiya+zod +zjrlKdLmftaTEMGURDIjooNygFOAania7UhLWOjhnzZFntLHoYmskF0C7om4CqPR +dhf1OuyaD436yQVdt3t/8sjBGuY4hQZ3PuMKDEsHEzmq8bYwfmDf3U8IsK39NbXO +Bh7Q3Gxxy0vTP2TRsGUbePh5ZveVtCJGe79tjtveLdEVdh+TsMB2xo/ZLFfjDPOx +6xvj5xKQtyou5YawuniHn5nrDWbyARQDTmVl7a5w5HvK3SbuVDrJsuLiRohfKyah +M17SAY5reFev6+piR+zT64zwTYdU20i28gEr +-----END OpenVPN tls-crypt-v2 client key----- diff --git a/test/ssl/tls-crypt-v2-server.key b/test/ssl/tls-crypt-v2-server.key new file mode 100644 index 0000000..e8152aa --- /dev/null +++ b/test/ssl/tls-crypt-v2-server.key @@ -0,0 +1,5 @@ +-----BEGIN OpenVPN tls-crypt-v2 server key----- +hn6VWraZG0o64iPI7faGYkwTMajjqhXqG2kC1X19jCl+aXhWPSgmDbRCT/pSfjLZ +a2pKXP6DGdi3nuINKuPw4655AcuMaBe4b45+zJz5xk92NnNosLaTsanXtjfBNOK5 +S/M1f4cLZViVsJTDSMcINK/RCclWVK+IFi//CThZQM8= +-----END OpenVPN tls-crypt-v2 server key----- diff --git a/test/unittests/unittests.vcxproj b/test/unittests/unittests.vcxproj index 1d1e793..fc8c7aa 100644 --- a/test/unittests/unittests.vcxproj +++ b/test/unittests/unittests.vcxproj @@ -1,143 +1,143 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - {18446924-20CC-4EB7-B639-A76C1422E5C2} - unittests - 8.1 - - - - Application - true - v140 - MultiByte - - - Application - false - v140 - true - MultiByte - - - Application - true - v140 - MultiByte - - - Application - false - v140 - true - MultiByte - - - - - - - - - - - - - - - - - - - - - - - TurnOffAllWarnings - Disabled - false - $(OVPN3_BUILD)\amd64\mbedtls\include;$(OVPN3_BUILD)\amd64\lz4\lib;$(GTEST_ROOT)\googletest\include;$(OVPN3_CORE);$(OVPN3_BUILD)\amd64\asio\asio\include;$(OVPN3_TAP_WINDOWS)\src;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_WARNINGS;NOMINMAX;_WIN32_WINNT=0x0600;USE_ASIO;ASIO_STANDALONE;USE_MBEDTLS;HAVE_LZ4;OPENVPN_FORCE_TUN_NULL;%(PreprocessorDefinitions) - - - true - lz4.lib;mbedtls.lib;Iphlpapi.lib;gtestd.lib;%(AdditionalDependencies) - $(OVPN3_BUILD)\amd64\mbedtls\library;$(GTEST_ROOT)\msvc\gtest\Debug\;%(AdditionalLibraryDirectories);$(OVPN3_BUILD)\amd64\lz4\lib - - - - - TurnOffAllWarnings - Disabled - false - $(OVPN3_ROOT)\deps\amd64\mbedtls\include;$(OVPN3_ROOT)\deps\amd64\lz4\lib;$(GTEST_ROOT)\googletest\include;$(OVPN3_ROOT)\core;$(OVPN3_ROOT)\deps\amd64\asio\asio\include;$(OVPN3_ROOT)\deps\amd64\tap-windows\src;%(AdditionalIncludeDirectories) - MultiThreadedDebug - _CRT_SECURE_NO_WARNINGS;NOMINMAX;_WIN32_WINNT=0x0600;USE_ASIO;ASIO_STANDALONE;USE_MBEDTLS;HAVE_LZ4;OPENVPN_FORCE_TUN_NULL;_DEBUG;%(PreprocessorDefinitions) - - - true - lz4.lib;mbedtls.lib;Iphlpapi.lib;gtest.lib;%(AdditionalDependencies) - $(OVPN3_ROOT)\deps\amd64\mbedtls\library;$(GTEST_ROOT)\googlemock\gtest\Debug\;%(AdditionalLibraryDirectories);$(OVPN3_ROOT)\deps\amd64\lz4\lib - - - - - TurnOffAllWarnings - MaxSpeed - true - true - false - $(OVPN3_BUILD)\amd64\mbedtls\include;$(OVPN3_BUILD)\amd64\lz4\lib;$(GTEST_ROOT)\googletest\include;$(OVPN3_CORE);$(OVPN3_BUILD)\amd64\asio\asio\include;$(OVPN3_TAP_WINDOWS)\src;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_WARNINGS;NOMINMAX;_WIN32_WINNT=0x0600;USE_ASIO;ASIO_STANDALONE;USE_MBEDTLS;HAVE_LZ4;OPENVPN_FORCE_TUN_NULL;%(PreprocessorDefinitions) - - - true - true - true - lz4.lib;mbedtls.lib;Iphlpapi.lib;gtest.lib;%(AdditionalDependencies) - $(OVPN3_BUILD)\amd64\mbedtls\library;$(GTEST_ROOT)\msvc\gtest\Release\;%(AdditionalLibraryDirectories);$(OVPN3_BUILD)\amd64\lz4\lib - - - - - TurnOffAllWarnings - MaxSpeed - true - true - false - $(OVPN3_ROOT)\deps\amd64\mbedtls\include;$(OVPN3_ROOT)\deps\amd64\lz4\lib;$(GTEST_ROOT)\googletest\include;$(OVPN3_ROOT)\core;$(OVPN3_ROOT)\deps\amd64\asio\asio\include;$(OVPN3_ROOT)\deps\amd64\tap-windows\src;%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_WARNINGS;NOMINMAX;_WIN32_WINNT=0x0600;USE_ASIO;ASIO_STANDALONE;USE_MBEDTLS;HAVE_LZ4;OPENVPN_FORCE_TUN_NULL;%(PreprocessorDefinitions) - - - true - true - true - lz4.lib;mbedtls.lib;Iphlpapi.lib;gtest.lib;%(AdditionalDependencies) - $(OVPN3_ROOT)\deps\amd64\mbedtls\library;$(GTEST_ROOT)\googlemock\gtest\Release\;%(AdditionalLibraryDirectories);$(OVPN3_ROOT)\deps\amd64\lz4\lib - - - - - - - - + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {18446924-20CC-4EB7-B639-A76C1422E5C2} + unittests + 10.0.17134.0 + + + + Application + true + v140 + MultiByte + + + Application + false + v140 + true + MultiByte + + + Application + true + v141 + MultiByte + + + Application + false + v140 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + + + TurnOffAllWarnings + Disabled + false + $(OVPN3_BUILD)\amd64\mbedtls\include;$(OVPN3_BUILD)\amd64\lz4\lib;$(GTEST_ROOT)\googletest\include;$(OVPN3_CORE);$(OVPN3_BUILD)\amd64\asio\asio\include;$(OVPN3_TAP_WINDOWS)\src;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_WARNINGS;NOMINMAX;_WIN32_WINNT=0x0600;USE_ASIO;ASIO_STANDALONE;USE_MBEDTLS;HAVE_LZ4;OPENVPN_FORCE_TUN_NULL;%(PreprocessorDefinitions) + + + true + lz4.lib;mbedtls.lib;Iphlpapi.lib;gtestd.lib;%(AdditionalDependencies) + $(OVPN3_BUILD)\amd64\mbedtls\library;$(GTEST_ROOT)\msvc\gtest\Debug\;%(AdditionalLibraryDirectories);$(OVPN3_BUILD)\amd64\lz4\lib + + + + + TurnOffAllWarnings + Disabled + false + $(OVPN3_ROOT)\deps\amd64\mbedtls\include;$(OVPN3_ROOT)\deps\amd64\lz4\lib;$(GTEST_ROOT)\googletest\include;$(OVPN3_ROOT)\core;$(OVPN3_ROOT)\deps\amd64\asio\asio\include;$(OVPN3_ROOT)\deps\amd64\tap-windows\src;%(AdditionalIncludeDirectories) + MultiThreadedDebug + _CRT_SECURE_NO_WARNINGS;NOMINMAX;_WIN32_WINNT=0x0600;USE_ASIO;ASIO_STANDALONE;USE_MBEDTLS;HAVE_LZ4;OPENVPN_FORCE_TUN_NULL;_DEBUG;%(PreprocessorDefinitions) + + + true + lz4.lib;mbedtls.lib;Iphlpapi.lib;gtest.lib;%(AdditionalDependencies) + $(OVPN3_ROOT)\deps\amd64\mbedtls\library;$(GTEST_ROOT)\googlemock\gtest\Debug\;%(AdditionalLibraryDirectories);$(OVPN3_ROOT)\deps\amd64\lz4\lib + + + + + TurnOffAllWarnings + MaxSpeed + true + true + false + $(OVPN3_BUILD)\amd64\mbedtls\include;$(OVPN3_BUILD)\amd64\lz4\lib;$(GTEST_ROOT)\googletest\include;$(OVPN3_CORE);$(OVPN3_BUILD)\amd64\asio\asio\include;$(OVPN3_TAP_WINDOWS)\src;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_WARNINGS;NOMINMAX;_WIN32_WINNT=0x0600;USE_ASIO;ASIO_STANDALONE;USE_MBEDTLS;HAVE_LZ4;OPENVPN_FORCE_TUN_NULL;%(PreprocessorDefinitions) + + + true + true + true + lz4.lib;mbedtls.lib;Iphlpapi.lib;gtest.lib;%(AdditionalDependencies) + $(OVPN3_BUILD)\amd64\mbedtls\library;$(GTEST_ROOT)\msvc\gtest\Release\;%(AdditionalLibraryDirectories);$(OVPN3_BUILD)\amd64\lz4\lib + + + + + TurnOffAllWarnings + MaxSpeed + true + true + false + $(OVPN3_ROOT)\deps\amd64\mbedtls\include;$(OVPN3_ROOT)\deps\amd64\lz4\lib;$(GTEST_ROOT)\googletest\include;$(OVPN3_ROOT)\core;$(OVPN3_ROOT)\deps\amd64\asio\asio\include;$(OVPN3_ROOT)\deps\amd64\tap-windows\src;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_WARNINGS;NOMINMAX;_WIN32_WINNT=0x0600;USE_ASIO;ASIO_STANDALONE;USE_MBEDTLS;HAVE_LZ4;OPENVPN_FORCE_TUN_NULL;%(PreprocessorDefinitions) + + + true + true + true + lz4.lib;mbedtls.lib;Iphlpapi.lib;gtest.lib;%(AdditionalDependencies) + $(OVPN3_ROOT)\deps\amd64\mbedtls\library;$(GTEST_ROOT)\googlemock\gtest\Release\;%(AdditionalLibraryDirectories);$(OVPN3_ROOT)\deps\amd64\lz4\lib + + + + + + + + \ No newline at end of file diff --git a/vars/vars-android b/vars/vars-android deleted file mode 100644 index 0f5a186..0000000 --- a/vars/vars-android +++ /dev/null @@ -1,15 +0,0 @@ -[ -z "$DEP_DIR" ] && export DEP_DIR=$HOME/src/android -export PLATFORM=android -export ABI=armeabi -export DEBUG_BUILD=0 -export OTHER_COMPILER_FLAGS="" -export CXX_COMPILER_FLAGS="-std=c++1y" -export LIB_OPT_LEVEL="-O3" -export LIB_FPIC="-fPIC" -export TC=$DEP_DIR/tc-arm -export PLATFORM_FLAGS="-march=armv5te --sysroot=$TC/sysroot" -export GPP_CMD="$TC/bin/arm-linux-androideabi-g++" -export GCC_CMD="$TC/bin/arm-linux-androideabi-gcc" - -[ -z "$VARS_SAVE_PATH" ] && VARS_SAVE_PATH="$PATH" -export PATH="$TC/bin:$TC/arm-linux-androideabi/bin:$VARS_SAVE_PATH" diff --git a/vars/vars-android-a7a b/vars/vars-android-a7a index edf0c7d..ac72bf1 100644 --- a/vars/vars-android-a7a +++ b/vars/vars-android-a7a @@ -7,7 +7,7 @@ export CXX_COMPILER_FLAGS="-std=c++1y" export LIB_OPT_LEVEL="-O3" export LIB_FPIC="-fPIC" export TC=$DEP_DIR/tc-arm -export PLATFORM_FLAGS="-D__LP32__ -D__ANDROID_API__=14 -march=armv7-a -mthumb -fomit-frame-pointer --sysroot=$TC/sysroot" +export PLATFORM_FLAGS="-D__LP32__ -march=armv7-a -mthumb -fomit-frame-pointer --sysroot=$TC/sysroot" export GPP_CMD="$TC/bin/arm-linux-androideabi-g++" export GCC_CMD="$TC/bin/arm-linux-androideabi-gcc" diff --git a/vars/vars-android-a8a b/vars/vars-android-a8a index 59c8dcf..0f29a51 100644 --- a/vars/vars-android-a8a +++ b/vars/vars-android-a8a @@ -7,7 +7,7 @@ export CXX_COMPILER_FLAGS="-std=c++1y" export LIB_OPT_LEVEL="-O3" export LIB_FPIC="-fPIC" export TC=$DEP_DIR/tc-arm64 -export PLATFORM_FLAGS="-D__ANDROID_API__=21 -march=armv8-a -fomit-frame-pointer --sysroot=$TC/sysroot" +export PLATFORM_FLAGS="-march=armv8-a -fomit-frame-pointer --sysroot=$TC/sysroot" export GPP_CMD="$TC/bin/aarch64-linux-android-g++" export GCC_CMD="$TC/bin/aarch64-linux-android-gcc" diff --git a/vars/vars-android-dbg b/vars/vars-android-dbg deleted file mode 100644 index 1c0713c..0000000 --- a/vars/vars-android-dbg +++ /dev/null @@ -1,15 +0,0 @@ -[ -z "$DEP_DIR" ] && export DEP_DIR=$HOME/src/android -export PLATFORM=android-dbg -export ABI=armeabi -export DEBUG_BUILD=1 -export OTHER_COMPILER_FLAGS="-g" -export CXX_COMPILER_FLAGS="-std=c++1y" -export LIB_OPT_LEVEL="-O0" -export LIB_FPIC="-fPIC" -export TC=$DEP_DIR/tc-arm -export PLATFORM_FLAGS="-march=armv5te --sysroot=$TC/sysroot" -export GPP_CMD="$TC/bin/arm-linux-androideabi-g++" -export GCC_CMD="$TC/bin/arm-linux-androideabi-gcc" - -[ -z "$VARS_SAVE_PATH" ] && VARS_SAVE_PATH="$PATH" -export PATH="$TC/bin:$TC/arm-linux-androideabi/bin:$VARS_SAVE_PATH" diff --git a/vars/vars-android-x86 b/vars/vars-android-x86 index 965a32b..6f6c74f 100644 --- a/vars/vars-android-x86 +++ b/vars/vars-android-x86 @@ -7,7 +7,7 @@ export CXX_COMPILER_FLAGS="-std=c++1y" export LIB_OPT_LEVEL="-O3" export LIB_FPIC="-fPIC" export TC=$DEP_DIR/tc-x86 -export PLATFORM_FLAGS="-D__LP32__ -D__ANDROID_API__=14 -march=i686 -fomit-frame-pointer --sysroot=$TC/sysroot" +export PLATFORM_FLAGS="-D__LP32__ -march=i686 -fomit-frame-pointer --sysroot=$TC/sysroot" export GPP_CMD="$TC/bin/i686-linux-android-g++" export GCC_CMD="$TC/bin/i686-linux-android-gcc" diff --git a/win/ovpn3-core.sln b/win/ovpn3-core.sln index e6fe3cd..5af53dc 100644 --- a/win/ovpn3-core.sln +++ b/win/ovpn3-core.sln @@ -1,44 +1,45 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cli", "ovpn3-core.vcxproj", "{1F891260-2039-494F-9777-EC5166AF31BC}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "unittests", "..\test\unittests\unittests.vcxproj", "{18446924-20CC-4EB7-B639-A76C1422E5C2}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|ARM = Debug|ARM - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|ARM = Release|ARM - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {1F891260-2039-494F-9777-EC5166AF31BC}.Debug|ARM.ActiveCfg = Debug|Win32 - {1F891260-2039-494F-9777-EC5166AF31BC}.Debug|x64.ActiveCfg = Debug|x64 - {1F891260-2039-494F-9777-EC5166AF31BC}.Debug|x64.Build.0 = Debug|x64 - {1F891260-2039-494F-9777-EC5166AF31BC}.Debug|x86.ActiveCfg = Debug|Win32 - {1F891260-2039-494F-9777-EC5166AF31BC}.Debug|x86.Build.0 = Debug|Win32 - {1F891260-2039-494F-9777-EC5166AF31BC}.Release|ARM.ActiveCfg = Release|Win32 - {1F891260-2039-494F-9777-EC5166AF31BC}.Release|x64.ActiveCfg = Release|x64 - {1F891260-2039-494F-9777-EC5166AF31BC}.Release|x64.Build.0 = Release|x64 - {1F891260-2039-494F-9777-EC5166AF31BC}.Release|x86.ActiveCfg = Release|Win32 - {1F891260-2039-494F-9777-EC5166AF31BC}.Release|x86.Build.0 = Release|Win32 - {18446924-20CC-4EB7-B639-A76C1422E5C2}.Debug|ARM.ActiveCfg = Debug|Win32 - {18446924-20CC-4EB7-B639-A76C1422E5C2}.Debug|x64.ActiveCfg = Debug|x64 - {18446924-20CC-4EB7-B639-A76C1422E5C2}.Debug|x64.Build.0 = Debug|x64 - {18446924-20CC-4EB7-B639-A76C1422E5C2}.Debug|x86.ActiveCfg = Debug|Win32 - {18446924-20CC-4EB7-B639-A76C1422E5C2}.Debug|x86.Build.0 = Debug|Win32 - {18446924-20CC-4EB7-B639-A76C1422E5C2}.Release|ARM.ActiveCfg = Release|Win32 - {18446924-20CC-4EB7-B639-A76C1422E5C2}.Release|x64.ActiveCfg = Release|x64 - {18446924-20CC-4EB7-B639-A76C1422E5C2}.Release|x64.Build.0 = Release|x64 - {18446924-20CC-4EB7-B639-A76C1422E5C2}.Release|x86.ActiveCfg = Release|Win32 - {18446924-20CC-4EB7-B639-A76C1422E5C2}.Release|x86.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28307.106 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cli", "ovpn3-core.vcxproj", "{1F891260-2039-494F-9777-EC5166AF31BC}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "unittests", "..\test\unittests\unittests.vcxproj", "{18446924-20CC-4EB7-B639-A76C1422E5C2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM = Debug|ARM + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|ARM = Release|ARM + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1F891260-2039-494F-9777-EC5166AF31BC}.Debug|ARM.ActiveCfg = Debug|x64 + {1F891260-2039-494F-9777-EC5166AF31BC}.Debug|x64.ActiveCfg = Debug|x64 + {1F891260-2039-494F-9777-EC5166AF31BC}.Debug|x64.Build.0 = Debug|x64 + {1F891260-2039-494F-9777-EC5166AF31BC}.Debug|x86.ActiveCfg = Debug|x64 + {1F891260-2039-494F-9777-EC5166AF31BC}.Release|ARM.ActiveCfg = Release|x64 + {1F891260-2039-494F-9777-EC5166AF31BC}.Release|x64.ActiveCfg = Release|x64 + {1F891260-2039-494F-9777-EC5166AF31BC}.Release|x64.Build.0 = Release|x64 + {1F891260-2039-494F-9777-EC5166AF31BC}.Release|x86.ActiveCfg = Release|x64 + {18446924-20CC-4EB7-B639-A76C1422E5C2}.Debug|ARM.ActiveCfg = Debug|Win32 + {18446924-20CC-4EB7-B639-A76C1422E5C2}.Debug|x64.ActiveCfg = Debug|x64 + {18446924-20CC-4EB7-B639-A76C1422E5C2}.Debug|x64.Build.0 = Debug|x64 + {18446924-20CC-4EB7-B639-A76C1422E5C2}.Debug|x86.ActiveCfg = Debug|Win32 + {18446924-20CC-4EB7-B639-A76C1422E5C2}.Debug|x86.Build.0 = Debug|Win32 + {18446924-20CC-4EB7-B639-A76C1422E5C2}.Release|ARM.ActiveCfg = Release|Win32 + {18446924-20CC-4EB7-B639-A76C1422E5C2}.Release|x64.ActiveCfg = Release|x64 + {18446924-20CC-4EB7-B639-A76C1422E5C2}.Release|x64.Build.0 = Release|x64 + {18446924-20CC-4EB7-B639-A76C1422E5C2}.Release|x86.ActiveCfg = Release|Win32 + {18446924-20CC-4EB7-B639-A76C1422E5C2}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {2C22702B-DF56-4CEB-9CA4-C999BD01FD03} + EndGlobalSection +EndGlobal diff --git a/win/ovpn3-core.vcxproj b/win/ovpn3-core.vcxproj index bd9421d..9f82684 100644 --- a/win/ovpn3-core.vcxproj +++ b/win/ovpn3-core.vcxproj @@ -1,14 +1,6 @@  - - Debug - Win32 - - - Release - Win32 - Debug x64 @@ -18,9 +10,6 @@ x64 - - - @@ -31,6 +20,8 @@ + + @@ -62,6 +53,7 @@ + @@ -81,6 +73,7 @@ + @@ -101,12 +94,14 @@ + + @@ -134,17 +129,19 @@ - + + + @@ -182,6 +179,7 @@ + @@ -210,6 +208,9 @@ + + + @@ -226,6 +227,7 @@ + @@ -233,14 +235,27 @@ - - + + + + + + + + + + + + + + + @@ -298,6 +313,7 @@ + @@ -325,6 +341,7 @@ + @@ -332,9 +349,12 @@ + + + @@ -342,6 +362,7 @@ + @@ -349,6 +370,8 @@ + + @@ -365,13 +388,15 @@ - + + + @@ -402,30 +427,20 @@ + + + {1F891260-2039-494F-9777-EC5166AF31BC} ovpn3core - 8.1 + 10.0.17134.0 cli - - Application - true - v140 - MultiByte - - - Application - false - v140 - true - MultiByte - Application true - v140 + v141 MultiByte @@ -442,21 +457,6 @@ - - - TurnOffAllWarnings - Disabled - false - $(OVPN3_BUILD)\amd64\mbedtls\include;$(OVPN3_TAP_WINDOWS)\src;$(OVPN3_BUILD)\amd64\asio\asio\include;$(OVPN3_BUILD)\amd64\lz4\lib;$(OVPN3_CORE);%(AdditionalIncludeDirectories) - _CRT_SECURE_NO_WARNINGS;NOMINMAX;_WIN32_WINNT=0x0600;USE_ASIO;ASIO_STANDALONE;USE_MBEDTLS;HAVE_LZ4;TAP_WIN_COMPONENT_ID=tap0901;%(PreprocessorDefinitions) - %(AdditionalOptions) - - - true - $(OVPN3_BUILD)\amd64\mbedtls\library;$(OVPN3_BUILD)\amd64\lz4\lib;%(AdditionalLibraryDirectories) - lz4.lib;mbedtls.lib;fwpuclnt.lib;ws2_32.lib;crypt32.lib;iphlpapi.lib;winmm.lib;advapi32.lib;wininet.lib;shell32.lib;ole32.lib;rpcrt4.lib;%(AdditionalDependencies) - - TurnOffAllWarnings @@ -476,24 +476,6 @@ NotSet - - - TurnOffAllWarnings - MaxSpeed - true - true - false - _CRT_SECURE_NO_WARNINGS;NOMINMAX;_WIN32_WINNT=0x0600;USE_ASIO;ASIO_STANDALONE;USE_MBEDTLS;HAVE_LZ4;TAP_WIN_COMPONENT_ID=tap0901;%(PreprocessorDefinitions) - $(OVPN3_BUILD)\amd64\mbedtls\include;$(OVPN3_TAP_WINDOWS)\src;$(OVPN3_BUILD)\amd64\asio\asio\include;$(OVPN3_BUILD)\amd64\lz4\lib;$(OVPN3_CORE);%(AdditionalIncludeDirectories) - - - true - true - true - $(OVPN3_BUILD)\amd64\mbedtls\library;$(OVPN3_BUILD)\amd64\lz4\lib;%(AdditionalLibraryDirectories) - lz4.lib;mbedtls.lib;fwpuclnt.lib;ws2_32.lib;crypt32.lib;iphlpapi.lib;winmm.lib;advapi32.lib;wininet.lib;shell32.lib;ole32.lib;rpcrt4.lib;%(AdditionalDependencies) - - TurnOffAllWarnings diff --git a/win/ovpn3-core.vcxproj.filters b/win/ovpn3-core.vcxproj.filters index dbc6aef..b57ff85 100644 --- a/win/ovpn3-core.vcxproj.filters +++ b/win/ovpn3-core.vcxproj.filters @@ -1,10 +1,6 @@  - - - - @@ -13,6 +9,8 @@ + + @@ -44,6 +42,7 @@ + @@ -63,6 +62,7 @@ + @@ -83,12 +83,14 @@ + + @@ -116,17 +118,19 @@ - + + + @@ -164,6 +168,7 @@ + @@ -192,6 +197,9 @@ + + + @@ -208,6 +216,7 @@ + @@ -215,14 +224,26 @@ - - + + + + + + + + + + + + + + @@ -280,6 +301,7 @@ + @@ -307,6 +329,7 @@ + @@ -314,9 +337,12 @@ + + + @@ -331,6 +357,8 @@ + + @@ -347,18 +375,21 @@ - + + + + @@ -370,10 +401,12 @@ + + @@ -383,5 +416,16 @@ + + + + + + + + + + + \ No newline at end of file