From 8e87aecebf66f50957e35966c547d77a6fb526ab Mon Sep 17 00:00:00 2001 From: Sergey Abramchuk Date: Sat, 12 Oct 2019 15:50:02 +0300 Subject: [PATCH] Squashed 'Sources/OpenVPNAdapter/Libraries/Vendors/openvpn/' changes from 275cf80efb..7db7a009b0 7db7a009b0 proto: Client complains about stub compressors 390154d0e4 Update Build instructions for OSX 1b92069834 deps: Update to mbedtls-2.7.12 8cab79540d compression: Extend compression alert to include server pushes 67b4641a99 CompressContext: Add is_any_stub() method cdf9e7bece compression: Issue an Event if compression is activated fa38064403 build script: added a new PROF type "auto" that tries to automatically determine the local platform 7ce7b52b7c MTRand: added OPENVPN_INSECURE_RANDOM compile flag that allows MTRand to masquerade as a secure RNG 85e7e49f72 MTRand: added constructor accepting an initialization seed 1fa3229a10 IPv4, IPv6: added #include 48e9217d26 vcxproj: add missing header file d2a2601b2f Wintun: unmap ring buffers e320bc63ff openssl: Improve OpenSSLContext fencing against multiple declarations 2f8fe2d318 openssl: Missing inline keyword in a couple of compat functions 32b984c0ff enum_dir: use a function template 725ee04593 VPNServerNetblock::Netblock::to_string(): show prefix_len 409d1c52b8 ManClientInstance::Send::describe_user(): added bool show_userprop parameter e05fc16b20 string::indent(): try to fix all the corner cases 4e1645ea80 RunContext: mark virtual Stop* async_stop() with override attribute e8b31c5454 cli: advertise "openurl" as supported SSO method 80b45731eb ICMPv6: added DEST_UNREACH code 679003094d AsioTimerSafe: refactor to allow as drop-in replacement for AsioTimer f7845578f1 RunContext: check for halt in timer closure 84483eda25 AsioPolySock: add support for socket shutdown 1b3402aec3 tcplinkcommon.hpp: added missing include 2e26c7565c time: added nanotime_t typedef c3c8ab7f6b string: added additional detail to split() comment 95ce4f22c8 string: added to_delim() method then redefined first_line() method to use it 448218b1e1 string: added add_leading() method e3b0bf4f5c MSF iterator: allow conversion from ordinary iterator and added exists() method 11412ac50a AsioPolySock: in remote_endpoint_str() method, test for alt_routing_enabled() 9fb4e705f9 Added TimeSkew to skew a time duration by a random flux 7496383002 write_binary_atomic: reduce the length of the temporary filename b31d9c0191 auth-token-user: increase size limit to 340 chars c82644c03a Added BufferLineIterator 115cb656b6 RandomAPI: added randbyte() and randbool() methods 4fa8348689 RunContext: ASIO SIGNAL message now shows signal name rather than number ebfce58513 Added StaticBuffer, a constant-length Buffer for writing that cannot be extended c8f9cb88a4 string::split(): call reserve() on return vector f15e566065 read_binary_unix_fast: should return an int (i.e. errno), not a bool 60501b4513 random: factor out rand32_distribute() from RandomAPI::randrange32() 90123495a5 wintun: get device interfaces list only once ec790df73b wintun: read packets in bulk 0f85d3f729 wintun: use correct io_context when performing initial read a6151cdeab wintun: use auto-reset events 29acfd95f3 libs: update ASIO to 1.14.0 438a0ef287 Remove outdated and unused android build files e9df57969f Merge remote-tracking branch 'origin/released' 44725ad094 ssl: Fix building with OpenSSL 1.0.2 efe3f1f635 version: Reset version reference for git master 8c79c06d94 Make tls-crypt/tls-cryptv2 compile with multiple compilation units 4d18aaeb88 Fix LLVM warnings reported during OS X build 8c9496bb4d Use const_cast for SSL_session_reused 33be562a39 Add missing override keywords to openssl/sslctx.hpp 2c5435a000 dcocli: use compile time define for Tun methods instead of hardcoded iproute 7c39088f00 Allow overriding reported HW_ADDR and support IV_PLAT_VER 7bb1ea19ee Move sending IV_UI_VER and IV_SSO to build_peer_info 23959fa705 Add reporting of IV_SSL_VER 63ab5b5e46 Only initialise static member in OpenSSLContext once ecebb40304 Merge remote-tracking branch 'origin/qa' 52c9702502 wintun: replace volatiles with atomics d720c7104c appveyor: install Strawberry perl 60a253a7ef appveyor: update to VS2019 48f2b5100b wintun: support for privilege separation 6f266be3d8 wintun: ring buffers support baa1ce2ccf vcxproj: bump VS version to 2019 98bfd037e3 tun/win: factor out ClientConfig into separate header aeb5ce0ad7 wintun: open device with SetupAPI 3998d303ce Finalizing the OpenVPN 3 Core library 3.3 release 728733aee7 deps/mbedtls: rebase "enable unsupported critical extensions" patch 43e36ca45a lib-version: update to mbedtls-2.7.11 4dbcd85e50 openssl/cipher.hpp: add missing include 69d72ed64f DCOTransport: Fix server side specific trunk handling ff732e3b5d Fix OpenVPN Core build with OpenSSL 1.1.0 0da42f393f Do not use deprecated OpenSSL 1.1.0 methods 35062c0b60 travis.yml: update environment 47046cf6d2 Merge branch 'qa' 6933c395a4 [OVPN3-423] cliconnect.hpp: fix reconnect on Windows after sleep 462c36c813 random_subnet(): added comment ac1d447156 IP::Addr::from_byte_string(): fixed bug for IPv6 case d6eaea3468 string::split(): minor implementation tweaks ca15b7cdf4 hexstr: added dump_hex() variant accepting void * 0e61a2afd7 SessionIDType::find_weak: added conflict parameter 089aec00b1 DCOTransport: new routing code for trunk links 5befbd430f build: added CAP=1 -- build with libcap eb85ada21e signals: added trivial signal_name() function f89013ef92 RunContext: don't try to catch SIGQUIT by default e0ee540135 SessionIDType: added hash() method f0e1f8aa42 logging: added basic components for logrotate fbb0c81f29 UMask: added UMaskDaemon, a umask context object appropriate for daemons 1c7bac90d9 build script: when building with DEBUG=1 on Linux, use -ggdb instead of -g 73cce80e43 OpenSSL: added openssl_reseed_rng() function 25780cf798 OpenSSL: fixed some memory leaks in CipherContextGCM and TokenEncrypt 168dba95f5 OpenSSL: define OPENSSL_SERVER_SNI when OpenSSL version is at least 1.1 84e78d8fed SNI: added OpenVPN client support for SNI (currently OpenSSL only) 310766b270 build: added MTLS_DIST setting 4eaa46a879 MbedTLS: added MBEDTLS_DISABLE_NAME_CONSTRAINTS preprocessor flag 16226d1b05 OpenSSLSign: updated for OpenSSL 1.1 aed0678c96 SSL: added SNI::Metadata, an abstract base class for packaging app-specific SNI metadata in AuthCert 001b731fe2 SNI: create SNI namespace and rename SNIHandlerBase -> SNI::HandlerBase 4bd5869305 README.rst: Make Windows-specific build steps up to date. ac365ee977 wintun: support for 0.4 9245056a2a wintun: support for 0.3 b73d484950 mbedtls: throw exception on unsupported SSL:Const::PEER_CERT_OPTIONAL option 1d6bae4b5b tcplinkcommon: bubble up real exception error c18c8bd156 tcpcli: ensure SSL Factory survives as long as TLS link 4192193087 tls: parse and load TLS specific CA 2a19b7fcff win/tuncli.hpp: fix Wintun padding calculation 44cb9f44da appveyor: make ReleaseOpenSSL default configuration 5485de19a2 win/impersonate: refactor impersonate logic 29a655147b win/tunsetup.hpp: remove unneeded parameter 61794b0efd win: link OpenSSL dynamically e569b84465 win/tuncli.hpp: fix indentation 374c57e708 frame_init.hpp: tweak wintun read buf size c3c45c9b38 tun: added Error::TUN_HALT for tun_error() signaling acd7af5e9a RandomAPI: added randrange32() method c1a7f8cc68 std::clamp() is useful but only available in C++17 and up, so we add our own clamp() f8c71ef1ce Minor change to Error::INACTIVE_TIMEOUT handler 3202ab5fce OpenSSLSign: renamed OpenSSLPKI::X509Base to OpenSSLPKI::X509 to conform to changes in OpenSSLPKI 8d767febb5 ReachabilityBase: added virtual destructor 6a4826965f MbedTLS: update json_override() prototype bee0d8d187 SSL: added SSLConst::SEND_CLIENT_CA_LIST server-side flag and implemented for OpenSSL 5eb39c1dea AuthCert: save the SNI name 3b34449d0e SSLAPI: auth_cert() can now be const a672e91631 SNI server-side: support additional JSON configuration settings 95e761f3cc OpenSSL PKI cleanup d5eb77c53c AuthCert::Fail cleanup 6e98b9aadc SSLAPI: move PKType from SSLConfigAPI into standalone header to avoid dependency inversion bbae814864 OpenSSL: added SNI implementation 5def1d23ab OpenSSLContext: in constructor, removed redundant if statement 1a0747e783 OpenSSLContext: in constructor, consolidate sslopt fixed flags eef9868816 OpenSSLContext::SSL::ssl_handshake_details(): include leaf-cert CN in details f9631cd90f AuthCert::Fail: use std::string for the reason string (instead of const char *) a17b77641f OpenSSLPKI::X509: copy constructor doesn't need erase() and define X509::Ptr 78cae5bb52 OpenSSLPKI::DH: copy constructor doesn't need erase() c0d43a4153 RCPtr: added static_pointer_cast() method 34a3f264f5 [OVPN-314] Add support for signalling SSO support via IV_SSO 7d112eb3e5 cli: enable utf8 console output 980ef1eff8 win/call.hpp: re-encode command output to utf8 fddb440e99 unicode.hpp: customize utf16 conversion routine 4d7c12ac4d [OVPN3-405] Support for non-ASCII profile path on Windows git-subtree-dir: Sources/OpenVPNAdapter/Libraries/Vendors/openvpn git-subtree-split: 7db7a009b0b4eca0fc3733c99c50aff7f7c2556f --- .travis.yml | 10 +- .travis/build-check.sh | 4 +- README.rst | 22 +- appveyor.yml | 16 +- client/ovpncli.cpp | 9 + client/ovpncli.hpp | 14 + deps/lib-versions | 8 +- ...unsupported-critical-extensions-in-r.patch | 41 +- openvpn/addr/ip.hpp | 2 +- openvpn/addr/ipv4.hpp | 1 + openvpn/addr/ipv6.hpp | 1 + openvpn/addr/randaddr.hpp | 1 + openvpn/apple/reachable.hpp | 2 + openvpn/asio/asiopolysock.hpp | 27 +- openvpn/asio/asiosignal.hpp | 1 - openvpn/auth/authcert.hpp | 64 ++- openvpn/buffer/buflineiter.hpp | 58 +++ openvpn/buffer/bufstatic.hpp | 45 ++ openvpn/buffer/safestr.hpp | 4 +- openvpn/client/cliconnect.hpp | 23 +- openvpn/client/clievent.hpp | 17 + openvpn/client/cliopt.hpp | 32 +- openvpn/client/cliopthelper.hpp | 4 +- openvpn/client/cliproto.hpp | 18 +- openvpn/client/remotelist.hpp | 4 +- openvpn/common/clamp.hpp | 39 ++ openvpn/common/daemon.hpp | 64 ++- openvpn/common/enumdir.hpp | 4 +- openvpn/common/extern.hpp | 4 + openvpn/common/file.hpp | 9 + openvpn/common/fileatomic.hpp | 2 +- openvpn/common/fileunix.hpp | 6 +- openvpn/common/hexstr.hpp | 15 + openvpn/common/logsetup.hpp | 36 ++ openvpn/common/msfind.hpp | 36 +- openvpn/common/rc.hpp | 6 + openvpn/common/runcontext.hpp | 23 +- openvpn/common/sess_id.hpp | 14 +- openvpn/common/signal_name.hpp | 50 ++ openvpn/common/string.hpp | 46 +- openvpn/common/umask.hpp | 8 + openvpn/common/usergroup.hpp | 10 + openvpn/common/version.hpp | 2 +- openvpn/compress/compress.hpp | 20 + openvpn/crypto/packet_id.hpp | 7 +- openvpn/crypto/tls_crypt.hpp | 18 +- openvpn/crypto/tls_crypt_v2.hpp | 12 +- openvpn/dco/dcocli.hpp | 127 ++--- openvpn/error/error.hpp | 4 + openvpn/frame/frame.hpp | 1 - openvpn/frame/frame_init.hpp | 6 - openvpn/ip/icmp6.hpp | 1 + openvpn/mbedtls/pki/pkctx.hpp | 12 +- openvpn/mbedtls/ssl/sslctx.hpp | 66 ++- openvpn/mbedtls/util/pem.hpp | 2 +- openvpn/openssl/compat.hpp | 25 +- openvpn/openssl/crypto/cipher.hpp | 4 +- openvpn/openssl/crypto/ciphergcm.hpp | 6 +- openvpn/openssl/pki/crl.hpp | 105 ++-- openvpn/openssl/pki/dh.hpp | 73 +-- openvpn/openssl/pki/pkey.hpp | 119 +++-- openvpn/openssl/pki/x509.hpp | 178 +++---- openvpn/openssl/pki/x509store.hpp | 48 +- openvpn/openssl/sign/pkcs7verify.hpp | 2 +- openvpn/openssl/sign/verify.hpp | 26 +- openvpn/openssl/ssl/sslctx.hpp | 456 +++++++++++++++--- openvpn/openssl/util/reseed.hpp | 40 ++ openvpn/openssl/util/tokenencrypt.hpp | 8 +- openvpn/pki/cclist.hpp | 8 +- openvpn/pki/pktype.hpp | 39 ++ openvpn/random/mtrandapi.hpp | 9 + openvpn/random/randapi.hpp | 37 ++ openvpn/random/randistrib.hpp | 37 ++ openvpn/server/manage.hpp | 2 +- openvpn/server/vpnservnetblock.hpp | 2 +- openvpn/ssl/kuparse.hpp | 24 +- openvpn/ssl/nscert.hpp | 20 +- openvpn/ssl/proto.hpp | 16 +- openvpn/ssl/sni_handler.hpp | 52 ++ openvpn/ssl/sni_metadata.hpp | 44 ++ openvpn/ssl/sslapi.hpp | 48 +- openvpn/ssl/sslchoose.hpp | 4 +- openvpn/ssl/sslconsts.hpp | 20 +- openvpn/time/asiotimersafe.hpp | 49 +- openvpn/time/epoch.hpp | 4 +- openvpn/time/skew.hpp | 53 ++ openvpn/transport/client/tcpcli.hpp | 22 +- openvpn/transport/tcplinkcommon.hpp | 18 +- openvpn/tun/persist/tunpersist.hpp | 8 +- openvpn/tun/win/client/clientconfig.hpp | 99 ++++ openvpn/tun/win/client/setupbase.hpp | 5 +- openvpn/tun/win/client/tuncli.hpp | 123 +---- openvpn/tun/win/client/tunsetup.hpp | 36 +- openvpn/tun/win/client/wintun.hpp | 318 ++++++++++++ openvpn/tun/win/ringbuffer.hpp | 172 +++++++ openvpn/tun/win/tunutil.hpp | 244 ++++++---- openvpn/tun/win/winproxy.hpp | 2 +- openvpn/win/call.hpp | 10 + openvpn/win/event.hpp | 108 +++++ openvpn/win/impersonate.hpp | 132 ++++- openvpn/win/scoped_handle.hpp | 15 +- openvpn/win/unicode.hpp | 34 +- scripts/android/build-all | 55 --- scripts/android/build-boost | 23 - scripts/android/build-lz4 | 25 - scripts/android/build-lzo | 22 - scripts/android/build-mbedtls | 30 -- scripts/android/build-minicrypto | 22 - scripts/android/build-openssl-small | 39 -- scripts/android/build-polarssl | 27 -- scripts/android/build-sdk | 47 -- scripts/android/build-snappy | 22 - scripts/android/build-toolchain | 66 --- scripts/build | 35 +- test/ovpncli/cli.cpp | 13 + test/ovpncli/go | 1 + test/unittests/core_tests.cpp | 1 + test/unittests/test_log.cpp | 4 + vars/android-sdk-path | 7 - win/build.py | 4 +- win/buildep.py | 2 +- win/ovpn3-core.sln | 7 + win/ovpn3-core.vcxproj | 56 ++- win/ovpn3-core.vcxproj.filters | 3 + 124 files changed, 3202 insertions(+), 1291 deletions(-) create mode 100644 openvpn/buffer/buflineiter.hpp create mode 100644 openvpn/buffer/bufstatic.hpp create mode 100644 openvpn/common/clamp.hpp create mode 100644 openvpn/common/logsetup.hpp create mode 100644 openvpn/common/signal_name.hpp create mode 100644 openvpn/openssl/util/reseed.hpp create mode 100644 openvpn/pki/pktype.hpp create mode 100644 openvpn/random/randistrib.hpp create mode 100644 openvpn/ssl/sni_handler.hpp create mode 100644 openvpn/ssl/sni_metadata.hpp create mode 100644 openvpn/time/skew.hpp create mode 100644 openvpn/tun/win/client/clientconfig.hpp create mode 100644 openvpn/tun/win/client/wintun.hpp create mode 100644 openvpn/tun/win/ringbuffer.hpp create mode 100644 openvpn/win/event.hpp delete mode 100755 scripts/android/build-all delete mode 100755 scripts/android/build-boost delete mode 100755 scripts/android/build-lz4 delete mode 100755 scripts/android/build-lzo delete mode 100755 scripts/android/build-mbedtls delete mode 100755 scripts/android/build-minicrypto delete mode 100755 scripts/android/build-openssl-small delete mode 100755 scripts/android/build-polarssl delete mode 100755 scripts/android/build-sdk delete mode 100755 scripts/android/build-snappy delete mode 100755 scripts/android/build-toolchain delete mode 100644 vars/android-sdk-path diff --git a/.travis.yml b/.travis.yml index d8551dc..6b44bdb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,19 +1,17 @@ -dist: trusty - +dist: bionic os: linux - language: cpp env: global: - 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" + - ASIO_VERSION="90f32660cd503494b3707840cfbd5434d8e9dabe" - 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" + - OPENSSL_VERSION="1.0.2s" - OPENSSL_CFLAGS="-I${PREFIX}/include" - OPENSSL_LIBS="-lssl -lcrypto" - COVERITY_BRANCH="master" @@ -22,9 +20,11 @@ matrix: include: - env: SSLLIB="openssl" os: osx + osx_image: xcode10.2 compiler: clang - env: SSLLIB="mbedtls" os: osx + osx_image: xcode10.2 compiler: clang - env: SSLLIB="openssl" RUN_COVERITY_SCAN="1" os: linux diff --git a/.travis/build-check.sh b/.travis/build-check.sh index e37d1f4..5c8f2fa 100755 --- a/.travis/build-check.sh +++ b/.travis/build-check.sh @@ -25,11 +25,11 @@ else fi LIBS="${SSL_LIBS} -llz4" -CXXFLAGS="-O3 -std=c++11 -Wall -pthread \ +CXXFLAGS="-O3 -std=c++14 -Wall -pthread \ -DOPENVPN_SHOW_SESSION_TOKEN -DHAVE_LZ4 \ -DUSE_ASIO -DASIO_STANDALONE -DASIO_NO_DEPRECATED ${SSL_CFLAGS}" -if [[ "${CC}" == "gcc"* ]]; then +if [ "${CC}" = "gcc" ]; then CXXFLAGS="${CXXFLAGS} -fwhole-program -flto=4" fi diff --git a/README.rst b/README.rst index c960dc2..550b0d4 100644 --- a/README.rst +++ b/README.rst @@ -117,6 +117,7 @@ not required for Mac builds. Build the dependencies:: + $ DL=~/Downloads $ OSX_ONLY=1 $O3/core/scripts/mac/build-all Now build the OpenVPN 3 client executable:: @@ -125,7 +126,7 @@ Now build the OpenVPN 3 client executable:: $ . vars/vars-osx64 $ . vars/setpath $ cd test/ovpncli - $ MTLS=1 LZ4=1 build cli + $ MTLS=1 LZ4=1 ASIO=1 build cli This will build the OpenVPN 3 client library with a small client wrapper (``cli``). It will also statically link in all external @@ -151,8 +152,9 @@ Building the OpenVPN 3 client on Windows Prerequisites: - - Visual Studio 2017 - - Python 2.7 +* Visual Studio 2017 +* Python 2.7 +* Perl (for building openssl) Clone the OpenVPN 3 source repo:: @@ -160,19 +162,19 @@ Clone the OpenVPN 3 source repo:: > c:\Temp>cd O3 > c:\Temp\O3>git clone https://github.com/OpenVPN/openvpn3.git core +Add environment variable ``O3`` with value ``c:\Temp\O3`` and reopen commmand prompt. + Download and build dependencies:: > c:\Temp\O3>cd core\win - > c:\Temp\O3\core\win>set O3=C:\Temp\O3 && python buildep.py + > c:\Temp\O3\core\win>set STATIC=1&& set DEBUG=1&& python buildep.py -Build test client:: +Now you can open project in Visual Studio. Project and solution files are +located in ``O3\core\win`` directory. - > c:\Temp\O3\core\win>set O3=C:\Temp\O3 && python build.py - -Visual Studio 2017 project and solution files are located in ``O3\core\win`` directory. -Before opening project you need to build dependencies and define OVPN3_ROOT -environmental variable (``C:\Temp\O3`` from example above). +You can also build the test client from command prompt:: + > c:\Temp\O3\core\win>set STATIC=1&& set DEBUG=1&& python build.py Testing ------- diff --git a/appveyor.yml b/appveyor.yml index 03de31a..06884d1 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,24 +1,32 @@ version: 1.0.{build} -image: Visual Studio 2017 +image: Visual Studio 2019 clone_folder: c:\ovpn3\core install: - pip install rfc6266 requests +- if not exist "C:\strawberry" choco install strawberryperl -y +- set PATH=C:\strawberry\c\bin;C:\strawberry\perl\site\bin;C:\strawberry\perl\bin;%PATH% environment: - MSVC_DIR: C:\Program Files (x86)\Microsoft Visual Studio\2017\Community + MSVC_DIR: C:\Program Files (x86)\Microsoft Visual Studio\2019\Community O3: C:\ovpn3 STATIC: 1 before_build: - cmd: cd win && python buildep.py +after_build: +- cmd: copy c:\ovpn3\deps\amd64\openssl\out32dll\ssleay32.dll c:\ovpn3\core\win\x64\ReleaseOpenSSL\ +- cmd: copy c:\ovpn3\deps\amd64\openssl\out32dll\libeay32.dll c:\ovpn3\core\win\x64\ReleaseOpenSSL\ + platform: x64 -configuration: Release +configuration: ReleaseOpenSSL artifacts: - - path: win\x64\Release\cli.exe + - path: win\x64\ReleaseOpenSSL\cli.exe + - path: win\x64\ReleaseOpenSSL\*.dll + diff --git a/client/ovpncli.cpp b/client/ovpncli.cpp index 8cc799d..c18fa31 100644 --- a/client/ovpncli.cpp +++ b/client/ovpncli.cpp @@ -436,7 +436,10 @@ namespace openvpn { std::string tls_version_min_override; std::string tls_cert_profile_override; std::string gui_version; + std::string sso_methods; bool allow_local_lan_access; + std::string hw_addr_override; + std::string platform_version; ProtoContextOptions::Ptr proto_context_options; PeerInfo::Set::Ptr extra_peer_info; HTTPProxyTransport::Options::Ptr http_proxy_options; @@ -689,6 +692,9 @@ namespace openvpn { state->tls_cert_profile_override = config.tlsCertProfileOverride; state->allow_local_lan_access = config.allowLocalLanAccess; state->gui_version = config.guiVersion; + state->sso_methods = config.ssoMethods; + state->platform_version = config.platformVersion; + state->hw_addr_override = config.hwAddrOverride; state->alt_proxy = config.altProxy; state->dco = config.dco; state->echo = config.echo; @@ -962,6 +968,9 @@ namespace openvpn { cc.tls_version_min_override = state->tls_version_min_override; cc.tls_cert_profile_override = state->tls_cert_profile_override; cc.gui_version = state->gui_version; + cc.sso_methods = state->sso_methods; + cc.hw_addr_override = state->hw_addr_override; + cc.platform_version = state->platform_version; cc.extra_peer_info = state->extra_peer_info; cc.stop = state->async_stop_local(); cc.allow_local_lan_access = state->allow_local_lan_access; diff --git a/client/ovpncli.hpp b/client/ovpncli.hpp index 1d74be2..e5b286e 100644 --- a/client/ovpncli.hpp +++ b/client/ovpncli.hpp @@ -169,6 +169,20 @@ namespace openvpn { // Passed to server as IV_GUI_VER. std::string guiVersion; + // Set to a comma seperated list of supported SSO mechanisms that may + // be signalled via INFO_PRE to the client. + // "openurl" is to continue authentication by opening an url in a browser + // "crtext" gives a challenge response in text format that needs to + // responded via control channel. ( + // Passed to the server as IV_SSO + std::string ssoMethods; + + // Override the string that is passed as IV_HWADDR to the server + std::string hwAddrOverride; + + // Set the string that is passed to the server as IV_PLAT_VER + std::string platformVersion; + // Use a different server than that specified in "remote" // option of profile std::string serverOverride; diff --git a/deps/lib-versions b/deps/lib-versions index 6747668..dee22d5 100644 --- a/deps/lib-versions +++ b/deps/lib-versions @@ -1,11 +1,11 @@ -export ASIO_VERSION=asio-1-13-0 -export ASIO_CSUM=54a1208d20f2104dbd6b7a04a9262f5ab649f4b7a9faf7eac4c2294e9e104c06 +export ASIO_VERSION=asio-1-14-0 +export ASIO_CSUM=bdb01a649c24d73ca4a836662e7af442d935313ed6deef6b07f17f3bc5f78d7a export LZ4_VERSION=lz4-1.8.3 export LZ4_CSUM=33af5936ac06536805f9745e0b6d61da606a1f8b4cc5c04dd3cbaca3b9b4fc43 -export MBEDTLS_VERSION=mbedtls-2.7.5 -export MBEDTLS_CSUM=a1302ad9094aabb9880d2755927b466a6bac8e02b68e04dee77321f3859e9b40 +export MBEDTLS_VERSION=mbedtls-2.7.12 +export MBEDTLS_CSUM=d3a36dbc9f607747daa6875c1ab2e41f49eff5fc99d3436b4f3ac90c89f3c143 export JSONCPP_VERSION=1.8.4 export JSONCPP_CSUM=c49deac9e0933bcb7044f08516861a2d560988540b23de2ac1ad443b219afdb6 diff --git a/deps/mbedtls/patches/0002-Enable-allowing-unsupported-critical-extensions-in-r.patch b/deps/mbedtls/patches/0002-Enable-allowing-unsupported-critical-extensions-in-r.patch index 3b1d159..f7f8f00 100644 --- a/deps/mbedtls/patches/0002-Enable-allowing-unsupported-critical-extensions-in-r.patch +++ b/deps/mbedtls/patches/0002-Enable-allowing-unsupported-critical-extensions-in-r.patch @@ -1,8 +1,7 @@ -From c6963e33209e7fd40d65513e06c1bbb20319abe3 Mon Sep 17 00:00:00 2001 +From 076f1437fe82de0b1f0ecf9a7ca031cd94c0c579 Mon Sep 17 00:00:00 2001 From: Lev Stipakov Date: Fri, 23 Feb 2018 17:12:49 +0200 -Subject: [PATCH 2/2] Enable allowing unsupported critical extensions in - runtime +Subject: [PATCH] Enable allowing unsupported critical extensions in runtime When compile time flag MBEDTLS_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION is not set, certificate parsing fails if certificate contains unsupported critical extension. @@ -55,7 +54,7 @@ index 408645ece..b116736f8 100644 /** diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h -index c6e453274..72374e36b 100644 +index 5fd6969da..1087ea166 100644 --- a/include/mbedtls/ssl.h +++ b/include/mbedtls/ssl.h @@ -696,6 +696,10 @@ struct mbedtls_ssl_config @@ -69,7 +68,7 @@ index c6e453274..72374e36b 100644 #if defined(MBEDTLS_SSL_RENEGOTIATION) int renego_max_records; /*!< grace period for renegotiation */ unsigned char renego_period[8]; /*!< value of the record counters -@@ -2275,6 +2279,24 @@ void mbedtls_ssl_conf_renegotiation_period( mbedtls_ssl_config *conf, +@@ -2298,6 +2302,24 @@ void mbedtls_ssl_conf_renegotiation_period( mbedtls_ssl_config *conf, const unsigned char period[8] ); #endif /* MBEDTLS_SSL_RENEGOTIATION */ @@ -95,7 +94,7 @@ index c6e453274..72374e36b 100644 * \brief Return the number of data bytes available to read * diff --git a/include/mbedtls/x509_crt.h b/include/mbedtls/x509_crt.h -index ac23cffe8..2e489915f 100644 +index e72231ee8..9df19e52c 100644 --- a/include/mbedtls/x509_crt.h +++ b/include/mbedtls/x509_crt.h @@ -90,6 +90,8 @@ typedef struct mbedtls_x509_crt @@ -220,10 +219,10 @@ index edea950f8..a756d2801 100644 static const mbedtls_oid_descriptor_t oid_ext_key_usage[] = { diff --git a/library/ssl_tls.c b/library/ssl_tls.c -index ca9b8c432..dba0d5122 100644 +index 1270ee9b8..2ce3f9b7d 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c -@@ -4656,6 +4656,9 @@ int mbedtls_ssl_parse_certificate( mbedtls_ssl_context *ssl ) +@@ -4668,6 +4668,9 @@ int mbedtls_ssl_parse_certificate( mbedtls_ssl_context *ssl ) mbedtls_x509_crt_init( ssl->session_negotiate->peer_cert ); @@ -233,7 +232,7 @@ index ca9b8c432..dba0d5122 100644 i += 3; while( i < ssl->in_hslen ) -@@ -6586,6 +6589,11 @@ void mbedtls_ssl_conf_renegotiation_period( mbedtls_ssl_config *conf, +@@ -6626,6 +6629,11 @@ void mbedtls_ssl_conf_renegotiation_period( mbedtls_ssl_config *conf, } #endif /* MBEDTLS_SSL_RENEGOTIATION */ @@ -246,18 +245,18 @@ index ca9b8c432..dba0d5122 100644 #if defined(MBEDTLS_SSL_CLI_C) void mbedtls_ssl_conf_session_tickets( mbedtls_ssl_config *conf, int use_tickets ) diff --git a/library/x509_crt.c b/library/x509_crt.c -index 6751da0d2..149149b96 100644 +index 3ad53a715..130b3ad1b 100644 --- a/library/x509_crt.c +++ b/library/x509_crt.c -@@ -530,6 +530,7 @@ static int x509_get_crt_ext( unsigned char **p, +@@ -539,6 +539,7 @@ static int x509_get_crt_ext( unsigned char **p, int ret; size_t len; unsigned char *end_ext_data, *end_ext_octet; + int is_supported; - if( ( ret = mbedtls_x509_get_ext( p, end, &crt->v3_ext, 3 ) ) != 0 ) - { -@@ -589,9 +590,9 @@ static int x509_get_crt_ext( unsigned char **p, + if( *p == end ) + return( 0 ); +@@ -593,9 +594,9 @@ static int x509_get_crt_ext( unsigned char **p, /* * Detect supported extensions */ @@ -269,7 +268,7 @@ index 6751da0d2..149149b96 100644 { /* No parser found, skip extension */ *p = end_ext_octet; -@@ -599,6 +600,10 @@ static int x509_get_crt_ext( unsigned char **p, +@@ -603,6 +604,10 @@ static int x509_get_crt_ext( unsigned char **p, #if !defined(MBEDTLS_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION) if( is_critical ) { @@ -280,7 +279,7 @@ index 6751da0d2..149149b96 100644 /* Data is marked as critical: fail */ return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + MBEDTLS_ERR_ASN1_UNEXPECTED_TAG ); -@@ -952,6 +957,7 @@ int mbedtls_x509_crt_parse_der( mbedtls_x509_crt *chain, const unsigned char *bu +@@ -956,6 +961,7 @@ int mbedtls_x509_crt_parse_der( mbedtls_x509_crt *chain, const unsigned char *bu prev = crt; mbedtls_x509_crt_init( crt->next ); @@ -315,10 +314,10 @@ index 000000000..7e0c56134 +OwQ6w1HweApjB46bGyILpGUi9MZhvCnoLWg+cN3/wQ== +-----END CERTIFICATE----- diff --git a/tests/suites/test_suite_x509parse.data b/tests/suites/test_suite_x509parse.data -index 406cf5931..212a2825a 100644 +index 0fe68cb06..e39f065e2 100644 --- a/tests/suites/test_suite_x509parse.data +++ b/tests/suites/test_suite_x509parse.data -@@ -1766,6 +1766,12 @@ X509 File parse (trailing spaces, OK) +@@ -1798,6 +1798,12 @@ X509 File parse (trailing spaces, OK) depends_on:MBEDTLS_ECDSA_C:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_SHA256_C:MBEDTLS_RSA_C x509parse_crt_file:"data_files/server7_trailing_space.crt":0 @@ -332,10 +331,10 @@ index 406cf5931..212a2825a 100644 depends_on:MBEDTLS_X509_USE_C x509_get_time:MBEDTLS_ASN1_UTC_TIME:"500101000000Z":0:1950:1:1:0:0:0 diff --git a/tests/suites/test_suite_x509parse.function b/tests/suites/test_suite_x509parse.function -index 06f010828..75936010f 100644 +index 584ee822b..c12a0e0ef 100644 --- a/tests/suites/test_suite_x509parse.function +++ b/tests/suites/test_suite_x509parse.function -@@ -437,6 +437,21 @@ exit: +@@ -448,6 +448,21 @@ exit: } /* END_CASE */ @@ -358,5 +357,5 @@ index 06f010828..75936010f 100644 void x509parse_crt( char *crt_data, char *result_str, int result ) { -- -2.18.0 +2.22.0.windows.1 diff --git a/openvpn/addr/ip.hpp b/openvpn/addr/ip.hpp index 1fda545..cd14e5c 100644 --- a/openvpn/addr/ip.hpp +++ b/openvpn/addr/ip.hpp @@ -268,7 +268,7 @@ namespace openvpn { } else { - a.ver = V4; + a.ver = V6; a.u.v6 = IPv6::Addr::from_byte_string(bytestr); } return a; diff --git a/openvpn/addr/ipv4.hpp b/openvpn/addr/ipv4.hpp index 03c7653..1fd55de 100644 --- a/openvpn/addr/ipv4.hpp +++ b/openvpn/addr/ipv4.hpp @@ -35,6 +35,7 @@ #include #include #include +#include #include namespace openvpn { diff --git a/openvpn/addr/ipv6.hpp b/openvpn/addr/ipv6.hpp index 9fedb64..d116934 100644 --- a/openvpn/addr/ipv6.hpp +++ b/openvpn/addr/ipv6.hpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include diff --git a/openvpn/addr/randaddr.hpp b/openvpn/addr/randaddr.hpp index 4de41c2..e8cdac5 100644 --- a/openvpn/addr/randaddr.hpp +++ b/openvpn/addr/randaddr.hpp @@ -53,6 +53,7 @@ namespace openvpn { } } + // bit positions between templ.prefix_len and prefix_len are randomized inline Route random_subnet(const Route& templ, const unsigned int prefix_len, RandomAPI& prng) diff --git a/openvpn/apple/reachable.hpp b/openvpn/apple/reachable.hpp index be0a700..abf808a 100644 --- a/openvpn/apple/reachable.hpp +++ b/openvpn/apple/reachable.hpp @@ -202,6 +202,8 @@ namespace openvpn { virtual Type vtype() const = 0; virtual Status vstatus(const SCNetworkReachabilityFlags flags) const = 0; + virtual ~ReachabilityBase() {} + CF::NetworkReachability reach; }; diff --git a/openvpn/asio/asiopolysock.hpp b/openvpn/asio/asiopolysock.hpp index d7f95d5..638152d 100644 --- a/openvpn/asio/asiopolysock.hpp +++ b/openvpn/asio/asiopolysock.hpp @@ -48,6 +48,12 @@ namespace openvpn { namespace AsioPolySock { + // for shutdown() + enum ShutdownFlags { + SHUTDOWN_SEND = (1<<0), + SHUTDOWN_RECV = (1<<1), + }; + class Base : public RC { public: @@ -66,6 +72,8 @@ namespace openvpn { virtual void close() = 0; + virtual void shutdown(const unsigned int flags) {} + virtual void tcp_nodelay() {} virtual void set_cloexec() {} @@ -171,6 +179,14 @@ namespace openvpn { } #endif + virtual void shutdown(const unsigned int flags) override + { + if (flags & SHUTDOWN_SEND) + socket.shutdown(openvpn_io::ip::tcp::socket::shutdown_send); + else if (flags & SHUTDOWN_RECV) + socket.shutdown(openvpn_io::ip::tcp::socket::shutdown_receive); + } + virtual void close() override { socket.close(); @@ -194,7 +210,8 @@ namespace openvpn { #if defined(OPENVPN_POLYSOCK_SUPPORTS_ALT_ROUTING) virtual std::string remote_endpoint_str() const override { - return "TCP ALT " + socket.to_string(); + const char *proto = (socket.alt_routing_enabled() ? "TCP ALT " : "TCP "); + return proto + socket.to_string(); } virtual bool alt_routing_enabled() override @@ -261,6 +278,14 @@ namespace openvpn { SockOpt::set_cloexec(fd); } + virtual void shutdown(const unsigned int flags) override + { + if (flags & SHUTDOWN_SEND) + socket.shutdown(openvpn_io::ip::tcp::socket::shutdown_send); + else if (flags & SHUTDOWN_RECV) + socket.shutdown(openvpn_io::ip::tcp::socket::shutdown_receive); + } + virtual void close() override { socket.close(); diff --git a/openvpn/asio/asiosignal.hpp b/openvpn/asio/asiosignal.hpp index 55bdb95..022c0b9 100644 --- a/openvpn/asio/asiosignal.hpp +++ b/openvpn/asio/asiosignal.hpp @@ -78,7 +78,6 @@ namespace openvpn { S_SIGINT | S_SIGTERM #ifndef OPENVPN_PLATFORM_WIN - | S_SIGQUIT | S_SIGHUP | S_SIGUSR1 | S_SIGUSR2 diff --git a/openvpn/auth/authcert.hpp b/openvpn/auth/authcert.hpp index 702f601..f22021a 100644 --- a/openvpn/auth/authcert.hpp +++ b/openvpn/auth/authcert.hpp @@ -35,14 +35,16 @@ #include #include #include +#include namespace openvpn { class OpenSSLContext; class MbedTLSContext; - struct AuthCert : public RC + class AuthCert : public RC { + public: // AuthCert needs to friend SSL implementation classes friend class OpenSSLContext; friend class MbedTLSContext; @@ -52,16 +54,18 @@ namespace openvpn { class Fail { public: - // ordered by priority + // Ordered by severity. If many errors are present, the + // most severe error will be returned by get_code(). enum Type { - OK=0, // OK MUST be 0 - OTHER, + OK=0, // OK MUST be 0 + EXPIRED, // less severe... BAD_CERT_TYPE, - EXPIRED, + CERT_FAIL, + SNI_ERROR, // more severe... N }; - void add_fail(const size_t depth, const Type new_code, const char *reason) + void add_fail(const size_t depth, const Type new_code, std::string reason) { if (new_code > code) code = new_code; @@ -69,7 +73,7 @@ namespace openvpn { errors.emplace_back(); std::string& err = errors[depth]; if (err.empty()) - err = reason; + err = std::move(reason); else if (err.find(reason) == std::string::npos) { err += ", "; @@ -111,19 +115,21 @@ namespace openvpn { return ret; } - static const char *render_code(const Type code) + static std::string render_code(const Type code) { switch (code) { case OK: return "OK"; - case OTHER: + case CERT_FAIL: default: return "CERT_FAIL"; case BAD_CERT_TYPE: return "BAD_CERT_TYPE"; case EXPIRED: return "EXPIRED"; + case SNI_ERROR: + return "SNI_ERROR"; } } @@ -143,6 +149,11 @@ namespace openvpn { return sn >= 0; } + bool sni_defined() const + { + return !sni.empty(); + } + bool cn_defined() const { return !cn.empty(); @@ -161,7 +172,7 @@ namespace openvpn { bool operator==(const AuthCert& other) const { - return cn == other.cn && sn == other.sn && !std::memcmp(issuer_fp, other.issuer_fp, sizeof(issuer_fp)); + return sni == other.sni && cn == other.cn && sn == other.sn && !std::memcmp(issuer_fp, other.issuer_fp, sizeof(issuer_fp)); } bool operator!=(const AuthCert& other) const @@ -172,6 +183,10 @@ namespace openvpn { std::string to_string() const { std::ostringstream os; + if (!sni.empty()) + os << "SNI=" << sni << ' '; + if (sni_metadata) + os << "SNI_CN=" << sni_metadata->sni_client_name(*this) << ' '; os << "CN=" << cn << " SN=" << sn << " ISSUER_FP=" << issuer_fp_str(false); @@ -194,6 +209,21 @@ namespace openvpn { return cn; } + // Allow sni_metadata object, if it exists, to generate the client name. + // Otherwise fall back to normalize_cn(). + std::string sni_client_name() const + { + if (sni_metadata) + return sni_metadata->sni_client_name(*this); + else + return normalize_cn(); + } + + const std::string& get_sni() const + { + return sni; + } + const std::string& get_cn() const { return cn; @@ -214,11 +244,11 @@ namespace openvpn { return std::move(x509_track); } - void add_fail(const size_t depth, const Fail::Type new_code, const char *reason) + void add_fail(const size_t depth, const Fail::Type new_code, std::string reason) { if (!fail) fail.reset(new Fail()); - fail->add_fail(depth, new_code, reason); + fail->add_fail(depth, new_code, std::move(reason)); } bool is_fail() const @@ -231,13 +261,23 @@ namespace openvpn { return fail.get(); } + std::string fail_str() const + { + if (fail) + return fail->to_string(true); + else + return "OK"; + } + private: + std::string sni; // SNI (server name indication) std::string cn; // common name long sn; // serial number unsigned char issuer_fp[20]; // issuer cert fingerprint std::unique_ptr fail; std::unique_ptr x509_track; + SNI::Metadata::UPtr sni_metadata; }; } diff --git a/openvpn/buffer/buflineiter.hpp b/openvpn/buffer/buflineiter.hpp new file mode 100644 index 0000000..5fa6d13 --- /dev/null +++ b/openvpn/buffer/buflineiter.hpp @@ -0,0 +1,58 @@ +// 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-2019 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 + +namespace openvpn { + + // Iterate over the lines in a buffer by returning + // a sub-buffer for each line. Zero-copy. + class BufferLineIterator + { + public: + BufferLineIterator(const ConstBuffer& buf) + : src(buf) + { + } + + // Returns a zero-length buffer at end of iteration + ConstBuffer next() + { + return src.read_alloc_buf(line_len()); + } + + private: + size_t line_len() const + { + const unsigned char *const data = src.c_data(); + size_t i = 0; + while (i < src.size()) + if (data[i++] == '\n') + break; + return i; + } + + ConstBuffer src; + }; + +} diff --git a/openvpn/buffer/bufstatic.hpp b/openvpn/buffer/bufstatic.hpp new file mode 100644 index 0000000..eee076f --- /dev/null +++ b/openvpn/buffer/bufstatic.hpp @@ -0,0 +1,45 @@ +// 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-2019 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 + +namespace openvpn { + + // constant-length Buffer for writing that cannot be extended + template + class StaticBuffer : public Buffer + { + public: + StaticBuffer() + : Buffer(data, N, false) + { + } + + StaticBuffer(const StaticBuffer&) = delete; + StaticBuffer& operator=(const StaticBuffer&) = delete; + + private: + unsigned char data[N]; + }; + +} diff --git a/openvpn/buffer/safestr.hpp b/openvpn/buffer/safestr.hpp index 18f87c8..c170215 100644 --- a/openvpn/buffer/safestr.hpp +++ b/openvpn/buffer/safestr.hpp @@ -71,12 +71,12 @@ namespace openvpn { return buf_to_string(data); } - const size_t length() const + size_t length() const { return data.size(); } - const bool empty() const + bool empty() const { return !length(); } diff --git a/openvpn/client/cliconnect.hpp b/openvpn/client/cliconnect.hpp index 7b6b68e..8399bff 100644 --- a/openvpn/client/cliconnect.hpp +++ b/openvpn/client/cliconnect.hpp @@ -460,6 +460,14 @@ namespace openvpn { stop(); } break; + case Error::TUN_REGISTER_RINGS_ERROR: + { + ClientEvent::Base::Ptr ev = new ClientEvent::TunSetupFailed(client->fatal_reason()); + client_options->events().add_event(std::move(ev)); + client_options->stats().error(Error::TUN_REGISTER_RINGS_ERROR); + stop(); + } + break; case Error::TUN_IFACE_CREATE: { ClientEvent::Base::Ptr ev = new ClientEvent::TunIfaceCreate(client->fatal_reason()); @@ -473,7 +481,7 @@ namespace openvpn { ClientEvent::Base::Ptr ev = new ClientEvent::TunIfaceDisabled(client->fatal_reason()); client_options->events().add_event(std::move(ev)); client_options->stats().error(Error::TUN_IFACE_DISABLED); - stop(); + queue_restart(5000); } break; case Error::PROXY_ERROR: @@ -529,7 +537,10 @@ namespace openvpn { ClientEvent::Base::Ptr ev = new ClientEvent::InactiveTimeout(); client_options->events().add_event(std::move(ev)); client_options->stats().error(Error::INACTIVE_TIMEOUT); - graceful_stop(); + + // explicit exit notify is sent earlier by + // ClientProto::Session::inactive_callback() + stop(); } break; case Error::TRANSPORT_ERROR: @@ -548,6 +559,14 @@ namespace openvpn { queue_restart(5000); } break; + case Error::TUN_HALT: + { + ClientEvent::Base::Ptr ev = new ClientEvent::TunHalt(client->fatal_reason()); + client_options->events().add_event(std::move(ev)); + client_options->stats().error(Error::TUN_HALT); + stop(); + } + break; case Error::RELAY: { ClientEvent::Base::Ptr ev = new ClientEvent::Relay(); diff --git a/openvpn/client/clievent.hpp b/openvpn/client/clievent.hpp index 26d4983..6a88cae 100644 --- a/openvpn/client/clievent.hpp +++ b/openvpn/client/clievent.hpp @@ -55,6 +55,7 @@ namespace openvpn { PAUSE, RESUME, RELAY, + COMPRESSION_ENABLED, UNSUPPORTED_FEATURE, // start of nonfatal errors, must be marked by NONFATAL_ERROR_START below @@ -68,6 +69,7 @@ namespace openvpn { TLS_VERSION_MIN, CLIENT_HALT, CLIENT_SETUP, + TUN_HALT, CONNECTION_TIMEOUT, INACTIVE_TIMEOUT, DYNAMIC_CHALLENGE, @@ -108,6 +110,7 @@ namespace openvpn { "PAUSE", "RESUME", "RELAY", + "COMPRESSION_ENABLED", "UNSUPPORTED_FEATURE", // nonfatal errors @@ -121,6 +124,7 @@ namespace openvpn { "TLS_VERSION_MIN", "CLIENT_HALT", "CLIENT_SETUP", + "TUN_HALT", "CONNECTION_TIMEOUT", "INACTIVE_TIMEOUT", "DYNAMIC_CHALLENGE", @@ -361,6 +365,11 @@ namespace openvpn { ClientRestart(std::string reason) : ReasonBase(CLIENT_RESTART, std::move(reason)) {} }; + struct TunHalt : public ReasonBase + { + TunHalt(std::string reason) : ReasonBase(TUN_HALT, std::move(reason)) {} + }; + struct RelayError : public ReasonBase { RelayError(std::string reason) : ReasonBase(RELAY_ERROR, std::move(reason)) {} @@ -456,6 +465,14 @@ namespace openvpn { } }; + struct CompressionEnabled : public ReasonBase + { + CompressionEnabled(std::string msg) + : ReasonBase(COMPRESSION_ENABLED, std::move(msg)) + { + } + }; + class Queue : public RC { public: diff --git a/openvpn/client/cliopt.hpp b/openvpn/client/cliopt.hpp index 955b839..e8561e4 100644 --- a/openvpn/client/cliopt.hpp +++ b/openvpn/client/cliopt.hpp @@ -120,8 +120,11 @@ namespace openvpn { struct Config { std::string gui_version; + std::string sso_methods; std::string server_override; std::string port_override; + std::string hw_addr_override; + std::string platform_version; Protocol proto_override; IPv6Setting ipv6; int conn_timeout = 0; @@ -308,6 +311,13 @@ namespace openvpn { synchronous_dns_lookup = config.synchronous_dns_lookup; +#ifdef OPENVPN_TLS_LINK + if (opt.exists("tls-ca")) + { + tls_ca = opt.cat("tls-ca"); + } +#endif + // init transport config const std::string session_name = load_transport_config(); @@ -534,14 +544,27 @@ namespace openvpn { // setenv UV_ options pi->append_foreign_set_ptr(pcc.peerInfoUV()); + // UI version + if (!config.gui_version.empty()) + pi->emplace_back("IV_GUI_VER", config.gui_version); + + // Supported SSO methods + if (!config.sso_methods.empty()) + pi->emplace_back("IV_SSO", config.sso_methods); + // MAC address if (pcc.pushPeerInfo()) { std::string hwaddr = get_hwaddr(); - if (!hwaddr.empty()) + if (!config.hw_addr_override.empty()) + pi->emplace_back("IV_HWADDR", config.hw_addr_override); + else if (!hwaddr.empty()) pi->emplace_back("IV_HWADDR", hwaddr); - } + pi->emplace_back ("IV_SSL", get_ssl_library_version()); + if (!config.platform_version.empty()) + pi->emplace_back("IV_PLAT_VER", config.platform_version); + } return pi; } @@ -716,7 +739,6 @@ namespace openvpn { cp->ssl_factory = cc->new_factory(); cp->load(opt, *proto_context_options, config.default_key_direction, false); cp->set_xmit_creds(!autologin || pcc.hasEmbeddedPassword() || autologin_sessions); - cp->gui_version = config.gui_version; cp->force_aes_cbc_ciphersuites = config.force_aes_cbc_ciphersuites; // also used to disable proto V2 cp->extra_peer_info = build_peer_info(config, pcc, autologin_sessions); cp->frame = frame; @@ -825,6 +847,7 @@ namespace openvpn { #ifdef OPENVPN_TLS_LINK if (transport_protocol.is_tls()) tcpconf->use_tls = true; + tcpconf->tls_ca = tls_ca; #endif #ifdef OPENVPN_GREMLIN tcpconf->gremlin_config = gremlin_config; @@ -881,6 +904,9 @@ namespace openvpn { DCO::Ptr dco; #ifdef OPENVPN_EXTERNAL_TRANSPORT_FACTORY ExternalTransport::Factory* extern_transport_factory; +#endif +#ifdef OPENVPN_TLS_LINK + std::string tls_ca; #endif }; } diff --git a/openvpn/client/cliopthelper.hpp b/openvpn/client/cliopthelper.hpp index 7d27fce..a5c97b3 100644 --- a/openvpn/client/cliopthelper.hpp +++ b/openvpn/client/cliopthelper.hpp @@ -558,8 +558,8 @@ namespace openvpn { // JSON config is aimed to users, therefore we do not export the raw private // key, but only some basic info - SSLConfigAPI::PKType priv_key_type = sslConfig->private_key_type(); - if (priv_key_type != SSLConfigAPI::PK_NONE) + PKType::Type priv_key_type = sslConfig->private_key_type(); + if (priv_key_type != PKType::PK_NONE) { root["key"] = Json::Value(Json::objectValue); root["key"]["type"] = Json::Value(sslConfig->private_key_type_string()); diff --git a/openvpn/client/cliproto.hpp b/openvpn/client/cliproto.hpp index 3694fa8..0cf41bf 100644 --- a/openvpn/client/cliproto.hpp +++ b/openvpn/client/cliproto.hpp @@ -506,7 +506,7 @@ namespace openvpn { { const Option* o = opt.get_ptr("auth-token-user"); if (o) - username = base64->decode(o->get(1, 256)); + username = base64->decode(o->get(1, 340)); // 255 chars after base64 decode } // auth-token @@ -601,6 +601,20 @@ namespace openvpn { // send the Connected event cli_events->add_event(connected_); + + // Issue an event if compression is enabled + CompressContext::Type comp_type = Base::conf().comp_ctx.type(); + if (comp_type != CompressContext::NONE + && !CompressContext::is_any_stub(comp_type)) + { + std::ostringstream msg; + msg << (proto_context_options->is_comp_asym() + ? "Asymmetric compression enabled. Server may send compressed data." + : "Compression enabled."); + msg << " This may be a potential security issue."; + ClientEvent::Base::Ptr ev = new ClientEvent::CompressionEnabled(msg.str()); + cli_events->add_event(std::move(ev)); + } } else OPENVPN_LOG("Options continuation..."); @@ -739,6 +753,8 @@ namespace openvpn { virtual void tun_error(const Error::Type fatal_err, const std::string& err_text) { + if (fatal_err == Error::TUN_HALT) + send_explicit_exit_notify(); if (fatal_err != Error::UNDEF) { fatal_ = fatal_err; diff --git a/openvpn/client/remotelist.hpp b/openvpn/client/remotelist.hpp index f9b3dca..7114cfa 100644 --- a/openvpn/client/remotelist.hpp +++ b/openvpn/client/remotelist.hpp @@ -759,7 +759,7 @@ namespace openvpn { // return the current primary index (into list) and raise an exception // if it is undefined - const size_t primary_index() const + size_t primary_index() const { const size_t pri = index.primary(); if (pri < list.size()) @@ -890,7 +890,7 @@ namespace openvpn { } else e->server_port = default_port; - if (o.size() >= 4+adj) + if (o.size() >= (size_t)(4+adj)) e->transport_protocol = Protocol::parse(o.get(3+adj, 16), Protocol::CLIENT_SUFFIX); else e->transport_protocol = default_proto; diff --git a/openvpn/common/clamp.hpp b/openvpn/common/clamp.hpp new file mode 100644 index 0000000..5ea766c --- /dev/null +++ b/openvpn/common/clamp.hpp @@ -0,0 +1,39 @@ +// 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-2019 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 + +// loose emulation of std::clamp for pre-C++17 + +namespace openvpn { + + template + T clamp(T value, T low, T high) + { + if (value < low) + return low; + else if (value > high) + return high; + else + return value; + } + +} diff --git a/openvpn/common/daemon.hpp b/openvpn/common/daemon.hpp index 142cc1a..2f18626 100644 --- a/openvpn/common/daemon.hpp +++ b/openvpn/common/daemon.hpp @@ -34,17 +34,52 @@ #include #include #include +#include namespace openvpn { OPENVPN_EXCEPTION(daemon_err); - inline void log_setup(const std::string& log_fn, - const SetUserGroup* user_group, - const bool log_append, - const int log_versions, - const bool stdin_to_dev_null, - const bool combine_out_err) + class LogReopen : public LogSetup + { + public: + LogReopen(const std::string& log_fn, + const bool combine_out_err) + : log_fn_(log_fn), + combine_out_err_(combine_out_err) + { + } + + virtual void reopen() const override + { + try { + // open redirection log file, but don't redirect yet + RedirectStd redir(std::string(), + log_fn_, + RedirectStd::FLAGS_APPEND, + RedirectStd::MODE_USER_GROUP, + combine_out_err_); + + // now do the redirect + redir.redirect(); + } + catch (const std::exception& e) + { + std::cerr << "LogReopen: " << e.what() << std::endl; + } + } + + private: + const std::string log_fn_; + const bool combine_out_err_; + }; + + inline LogSetup::Ptr log_setup(const std::string& log_fn, + const SetUserGroup* user_group, + const bool log_append, + const int log_versions, + const bool stdin_to_dev_null, + const bool combine_out_err) { if (!log_append && log_versions >= 1) log_rotate(log_fn, log_versions); @@ -62,6 +97,12 @@ namespace openvpn { { } redir.redirect(); + + // possibly return a LogReopen object + if (!log_versions) + return LogSetup::Ptr(new LogReopen(log_fn, combine_out_err)); + else + return LogSetup::Ptr(); } inline void daemonize() @@ -70,13 +111,14 @@ namespace openvpn { throw daemon_err("daemon() failed"); } - inline void daemonize(const std::string& log_fn, - const SetUserGroup* user_group, - const bool log_append, - const int log_versions) + inline LogSetup::Ptr daemonize(const std::string& log_fn, + const SetUserGroup* user_group, + const bool log_append, + const int log_versions) { - log_setup(log_fn, user_group, log_append, log_versions, true, true); + LogSetup::Ptr ret = log_setup(log_fn, user_group, log_append, log_versions, true, true); daemonize(); + return ret; } inline void write_pid(const std::string& fn) diff --git a/openvpn/common/enumdir.hpp b/openvpn/common/enumdir.hpp index 00ef6ed..fb8cfef 100644 --- a/openvpn/common/enumdir.hpp +++ b/openvpn/common/enumdir.hpp @@ -34,13 +34,13 @@ #include #include #include -#include namespace openvpn { OPENVPN_EXCEPTION(enum_dir_error); + template inline bool enum_dir(const std::string& dirname, - Function func) + F func) { unique_ptr_del dir(::opendir(dirname.c_str()), [](DIR* d) { ::closedir(d); }); if (!dir) diff --git a/openvpn/common/extern.hpp b/openvpn/common/extern.hpp index 035a977..bcf7a25 100644 --- a/openvpn/common/extern.hpp +++ b/openvpn/common/extern.hpp @@ -23,6 +23,10 @@ #define OPENVPN_COMMON_EXTERN_H #ifndef OPENVPN_EXTERN +// Remember that OPENVPN_EXTERN was not defined since something like +// #if OPENVPN_EXTERN == extern or OPENVPN_EXTERN == "" is not allowed +// in C/C++ preprocessor +#define OPENVPN_NO_EXTERN #define OPENVPN_EXTERN #endif diff --git a/openvpn/common/file.hpp b/openvpn/common/file.hpp index e53eb77..44be84b 100644 --- a/openvpn/common/file.hpp +++ b/openvpn/common/file.hpp @@ -35,6 +35,10 @@ #include #include +#if defined(OPENVPN_PLATFORM_WIN) +#include +#endif + namespace openvpn { OPENVPN_UNTAGGED_EXCEPTION(file_exception); @@ -61,7 +65,12 @@ namespace openvpn { const std::uint64_t max_size = 0, const unsigned int buffer_flags = 0) { +#if defined(OPENVPN_PLATFORM_WIN) + Win::UTF16 filenamew(Win::utf16(filename)); + std::ifstream ifs(filenamew.get(), std::ios::binary); +#else std::ifstream ifs(filename.c_str(), std::ios::binary); +#endif if (!ifs) OPENVPN_THROW(open_file_error, "cannot open for read: " << filename); diff --git a/openvpn/common/fileatomic.hpp b/openvpn/common/fileatomic.hpp index 009bc35..2369451 100644 --- a/openvpn/common/fileatomic.hpp +++ b/openvpn/common/fileatomic.hpp @@ -55,7 +55,7 @@ namespace openvpn { // generate temporary filename unsigned char data[16]; rng.rand_fill(data); - const std::string tfn = path::join(tmpdir, '.' + path::basename(fn) + '.' + render_hex(data, sizeof(data))); + const std::string tfn = path::join(tmpdir, '.' + path::basename(fn).substr(0, 64) + '.' + render_hex(data, sizeof(data))); // write to temporary file write_binary_unix(tfn, mode, mtime_ns, buf); diff --git a/openvpn/common/fileunix.hpp b/openvpn/common/fileunix.hpp index 4a0584f..a1901cb 100644 --- a/openvpn/common/fileunix.hpp +++ b/openvpn/common/fileunix.hpp @@ -158,12 +158,12 @@ namespace openvpn { return bp; } - inline bool read_binary_unix_fast(const std::string& fn, - Buffer& out) + inline int read_binary_unix_fast(const std::string& fn, + Buffer& out) { ScopedFD fd(::open(fn.c_str(), O_RDONLY|O_CLOEXEC)); if (!fd.defined()) - return errno; + return errno; const ssize_t status = ::read(fd(), out.data_end(), out.remaining(0)); if (status < 0) return errno; diff --git a/openvpn/common/hexstr.hpp b/openvpn/common/hexstr.hpp index 3024fac..a8a1732 100644 --- a/openvpn/common/hexstr.hpp +++ b/openvpn/common/hexstr.hpp @@ -279,6 +279,21 @@ namespace openvpn { return os.str(); } + /** + * Renders a combined hexadecimal and character dump of a buffer, + * with the typical 16 bytes split between hexadecimal and character + * separation per line. + * + * @param data Void pointer to the buffer to dump. + * @param size Size of the buffer to render. + * + * @return Returns a string containing a preformatted output of the + * hexadecimal dump. + */ + inline std::string dump_hex(void* data, size_t size) + { + return dump_hex((const unsigned char *)data, size); + } /** * Renders a combined hexadecimal and character dump of a std::string buffer, diff --git a/openvpn/common/logsetup.hpp b/openvpn/common/logsetup.hpp new file mode 100644 index 0000000..bffd6d9 --- /dev/null +++ b/openvpn/common/logsetup.hpp @@ -0,0 +1,36 @@ +// 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-2019 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 + +namespace openvpn { + + class LogSetup : public RC + { + public: + typedef RCPtr Ptr; + + virtual void reopen() const = 0; + }; + +} diff --git a/openvpn/common/msfind.hpp b/openvpn/common/msfind.hpp index b348301..eb3588d 100644 --- a/openvpn/common/msfind.hpp +++ b/openvpn/common/msfind.hpp @@ -29,16 +29,23 @@ namespace openvpn { namespace MSF { - template + template class Iter : public ITERATOR { public: + template Iter(const MAP_SET& ms, ITERATOR&& iter) : ITERATOR(std::move(iter)), exists_(*this != ms.end()) { } + Iter(ITERATOR&& iter) + : ITERATOR(std::move(iter)), + exists_(true) + { + } + explicit operator bool() const { return exists_; @@ -50,11 +57,34 @@ namespace openvpn { // Like ordinary map/set find, but returns an iterator // that defines an operator bool() method for testing if - // the iterator is defined, i.e. iter != map_or_set.end() + // the iterator is defined, so instead of: + // + // if (iter != map.end()) + // do_stuff(); + // + // you can say: + // + // if (iter) + // do_stuff(); + // template inline auto find(MAP_SET& ms, const KEY& k) { - return Iter(ms, ms.find(k)); + return Iter(ms, ms.find(k)); + } + + // Does key exist in map/set? + template + inline bool exists(MAP_SET& ms, const KEY& k) + { + return ms.find(k) != ms.end(); + } + + // Convert an ordinary, dereferenceable iterator to an MSF::Iter + template + inline auto iter(ITERATOR i) + { + return Iter(std::move(i)); } } } diff --git a/openvpn/common/rc.hpp b/openvpn/common/rc.hpp index 8627b8f..a26942f 100644 --- a/openvpn/common/rc.hpp +++ b/openvpn/common/rc.hpp @@ -196,6 +196,12 @@ namespace openvpn { return px != rhs.px; } + template + RCPtr static_pointer_cast() const noexcept + { + return RCPtr(static_cast(px)); + } + template RCPtr dynamic_pointer_cast() const noexcept { diff --git a/openvpn/common/runcontext.hpp b/openvpn/common/runcontext.hpp index a3e408d..f1088d6 100644 --- a/openvpn/common/runcontext.hpp +++ b/openvpn/common/runcontext.hpp @@ -37,6 +37,7 @@ #include #include #include // for std::is_nothrow_move_constructible +#include #include #include @@ -45,10 +46,12 @@ #include #include #include +#include #include #include #include #include +#include #ifdef ASIO_HAS_LOCAL_SOCKETS #include @@ -131,6 +134,11 @@ namespace openvpn { async_stop_ = async_stop; } + void set_log_reopen(LogSetup::Ptr lr) + { + log_reopen = std::move(lr); + } + void set_thread(const unsigned int unit, std::thread* thread) { while (threadlist.size() <= unit) @@ -312,7 +320,7 @@ namespace openvpn { stats = stats_arg; } - virtual Stop* async_stop() + virtual Stop* async_stop() override { return async_stop_; } @@ -342,14 +350,11 @@ namespace openvpn { { if (!error && !halt) { - OPENVPN_LOG("ASIO SIGNAL " << signum); + OPENVPN_LOG("ASIO SIGNAL: " << signal_name(signum)); switch (signum) { case SIGINT: case SIGTERM: -#if !defined(OPENVPN_PLATFORM_WIN) - case SIGQUIT: -#endif cancel(); break; #if !defined(OPENVPN_PLATFORM_WIN) @@ -358,6 +363,11 @@ namespace openvpn { OPENVPN_LOG(stats->dump()); signal_rearm(); break; + case SIGHUP: + if (log_reopen) + log_reopen->reopen(); + signal_rearm(); + break; #endif default: signal_rearm(); @@ -385,7 +395,7 @@ namespace openvpn { exit_timer.expires_after(Time::Duration::seconds(n_sec)); exit_timer.async_wait([self=Ptr(this)](const openvpn_io::error_code& error) { - if (error) + if (error || self->halt) return; OPENVPN_LOG("DEBUG EXIT"); self->cancel(); @@ -420,6 +430,7 @@ namespace openvpn { // logging Log::Context log_context; Log::Context::Wrapper log_wrap; // must be constructed after log_context + LogSetup::Ptr log_reopen; protected: volatile bool halt = false; diff --git a/openvpn/common/sess_id.hpp b/openvpn/common/sess_id.hpp index 8fe303c..e333596 100644 --- a/openvpn/common/sess_id.hpp +++ b/openvpn/common/sess_id.hpp @@ -115,6 +115,12 @@ namespace openvpn { return u.dataz[0]; } + template + void hash(HASH& h) const + { + h(u.dataz[0]); + } + // Use a URL-safe base64 encoding. std::string to_string() const { @@ -160,13 +166,15 @@ namespace openvpn { } // Find an element in an unordered map (keyed by Session ID) - // using weak equality. + // using weak equality. If conflict is true, only return + // element that is present by weak equality, but which is + // not equal to *this by strong equality. template - const SessionIDType* find_weak(const UNORDERED_MAP& m) const + const SessionIDType* find_weak(const UNORDERED_MAP& m, const bool conflict) const { const size_t bi = m.bucket(*this); for (auto i = m.cbegin(bi); i != m.cend(bi); ++i) - if (shortform() == i->first.shortform()) + if (shortform() == i->first.shortform() && (!conflict || *this != i->first)) return &i->first; return nullptr; } diff --git a/openvpn/common/signal_name.hpp b/openvpn/common/signal_name.hpp new file mode 100644 index 0000000..b69068e --- /dev/null +++ b/openvpn/common/signal_name.hpp @@ -0,0 +1,50 @@ +// 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-2019 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 { + + inline std::string signal_name(const int signum) + { + switch (signum) + { + case SIGINT: + return "SIGINT"; + case SIGTERM: + return "SIGTERM"; + case SIGHUP: + return "SIGHUP"; + case SIGUSR1: + return "SIGUSR1"; + case SIGUSR2: + return "SIGUSR2"; + case SIGPIPE: + return "SIGPIPE"; + default: + return std::to_string(signum); + } + } + +} diff --git a/openvpn/common/string.hpp b/openvpn/common/string.hpp index 4d7b508..4099f4b 100644 --- a/openvpn/common/string.hpp +++ b/openvpn/common/string.hpp @@ -150,6 +150,21 @@ namespace openvpn { return false; } + // Prepend leading characters (c) to str to obtain a minimum string length (min_len). + // Useful for adding leading zeros to numeric values or formatting tables. + inline std::string add_leading(const std::string& str, const size_t min_len, const char c) + { + if (min_len <= str.length()) + return str; + size_t len = min_len - str.length(); + std::string ret; + ret.reserve(min_len); + while (len--) + ret += c; + ret += str; + return ret; + } + // make sure that string ends with char c, if not append it inline std::string add_trailing_copy(const std::string& str, const char c) { @@ -235,16 +250,23 @@ namespace openvpn { return str.find_first_of('\n') != std::string::npos; } - // return the first line (without newline) of a multi-line string - inline std::string first_line(const std::string& str) + // Return string up to a delimiter (without the delimiter). + // Returns the entire string if no delimiter is found. + inline std::string to_delim(const std::string& str, const char delim) { - const size_t pos = str.find_first_of('\n'); + const size_t pos = str.find_first_of(delim); if (pos != std::string::npos) return str.substr(0, pos); else return str; } + // return the first line (without newline) of a multi-line string + inline std::string first_line(const std::string& str) + { + return to_delim(str, '\n'); + } + // Define a common interpretation of what constitutes a space character. // Return true if c is a space char. inline bool is_space(const char c) @@ -385,12 +407,16 @@ namespace openvpn { // indent a multiline string inline std::string indent(const std::string& str, const int first, const int remaining) { - std::string ret = spaces(first); + std::string ret; + int n_spaces = first; for (auto &c : str) { + if (n_spaces) + ret += spaces(n_spaces); + n_spaces = 0; ret += c; if (c == '\n') - ret += spaces(remaining); + n_spaces = remaining; } return ret; } @@ -447,7 +473,8 @@ namespace openvpn { } // Split a string on sep delimiter. The size of the - // returned string list will be at most maxsplit + 1. + // returned string vector will be at least 1 and at + // most maxsplit + 1 (unless maxsplit is passed as -1). inline std::vector split(const std::string& str, const char sep, const int maxsplit = -1) @@ -456,13 +483,16 @@ namespace openvpn { int nterms = 0; std::string term; - for (auto &c : str) + if (maxsplit >= 0) + ret.reserve(maxsplit + 1); + + for (const auto c : str) { if (c == sep && (maxsplit < 0 || nterms < maxsplit)) { ret.push_back(std::move(term)); ++nterms; - term = ""; + term.clear(); } else term += c; diff --git a/openvpn/common/umask.hpp b/openvpn/common/umask.hpp index 5aecda4..c1630b9 100644 --- a/openvpn/common/umask.hpp +++ b/openvpn/common/umask.hpp @@ -56,5 +56,13 @@ namespace openvpn { { } }; + + struct UMaskDaemon : public UMask + { + UMaskDaemon() + : UMask(S_IWOTH) + { + } + }; } #endif diff --git a/openvpn/common/usergroup.hpp b/openvpn/common/usergroup.hpp index 6ba459c..df61a6b 100644 --- a/openvpn/common/usergroup.hpp +++ b/openvpn/common/usergroup.hpp @@ -76,6 +76,16 @@ namespace openvpn { } } + const std::string& user() const + { + return user_name; + } + + const std::string& group() const + { + return group_name; + } + void activate() const { if (gr) diff --git a/openvpn/common/version.hpp b/openvpn/common/version.hpp index e7dd107..99720f8 100644 --- a/openvpn/common/version.hpp +++ b/openvpn/common/version.hpp @@ -24,5 +24,5 @@ #pragma once #ifndef OPENVPN_VERSION -#define OPENVPN_VERSION "3.2 (qa:d87f5bbc04)" +#define OPENVPN_VERSION "3.git:master" #endif diff --git a/openvpn/compress/compress.hpp b/openvpn/compress/compress.hpp index 8c72143..b6a82be 100644 --- a/openvpn/compress/compress.hpp +++ b/openvpn/compress/compress.hpp @@ -511,6 +511,26 @@ namespace openvpn { } } + /** + * Checks if the compression type is one of the available stub modes + * + * @param t The CompressContext::Type value + * @return Returns true if the type is one of the *_STUB{,v2} types, + * otherwise false. + */ + static bool is_any_stub(const Type t) + { + switch (t) + { + case LZO_STUB: + case COMP_STUB: + case COMP_STUBv2: + return true; + default: + return false; + } + } + static void init_static() { #ifndef NO_LZO diff --git a/openvpn/crypto/packet_id.hpp b/openvpn/crypto/packet_id.hpp index 2e8e157..df6a19e 100644 --- a/openvpn/crypto/packet_id.hpp +++ b/openvpn/crypto/packet_id.hpp @@ -83,11 +83,14 @@ namespace openvpn { static size_t size(const int form) { if (form == PacketID::LONG_FORM) - return sizeof(id_t) + sizeof(net_time_t); + return longidsize; else - return sizeof(id_t); + return shortidsize; } + constexpr static size_t shortidsize = sizeof(id_t); + constexpr static size_t longidsize = sizeof(id_t) + sizeof(net_time_t); + bool is_valid() const { return id != UNDEF; diff --git a/openvpn/crypto/tls_crypt.hpp b/openvpn/crypto/tls_crypt.hpp index f95ab00..e2c74a0 100644 --- a/openvpn/crypto/tls_crypt.hpp +++ b/openvpn/crypto/tls_crypt.hpp @@ -192,17 +192,17 @@ namespace openvpn { virtual TLSCryptInstance::Ptr new_obj_recv() = 0; - static const size_t hmac_offset; + // This is the size of the header in a TLSCrypt-wrapped packets, + // excluding the HMAC. Format: + // + // [OP] [PSID] [PID] [HMAC] [...] + // + + constexpr const static size_t hmac_offset = 1 + ProtoSessionID::SIZE + PacketID::longidsize; + }; - // 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 { diff --git a/openvpn/crypto/tls_crypt_v2.hpp b/openvpn/crypto/tls_crypt_v2.hpp index 41d5bb1..00507fc 100644 --- a/openvpn/crypto/tls_crypt_v2.hpp +++ b/openvpn/crypto/tls_crypt_v2.hpp @@ -33,6 +33,9 @@ #include namespace openvpn { + constexpr static const char* tls_crypt_v2_server_key_name = "OpenVPN tls-crypt-v2 server key"; + constexpr static const char* tls_crypt_v2_client_key_name = "OpenVPN tls-crypt-v2 client key"; + class TLSCryptV2ServerKey { public: @@ -79,10 +82,8 @@ namespace openvpn { 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 { @@ -133,7 +134,8 @@ namespace openvpn { 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)) + 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()); @@ -150,12 +152,8 @@ namespace openvpn { 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. // diff --git a/openvpn/dco/dcocli.hpp b/openvpn/dco/dcocli.hpp index bf3e300..c026c98 100644 --- a/openvpn/dco/dcocli.hpp +++ b/openvpn/dco/dcocli.hpp @@ -41,11 +41,9 @@ #include #include #include -#include #ifdef ENABLE_PG #include -#include #endif // client-side DCO (Data Channel Offload) module for Linux/kovpn @@ -68,6 +66,7 @@ namespace openvpn { DCO::TunConfig tun; int trunk_unit = -1; + unsigned int ping_restart_override = 0; virtual TunClientFactory::Ptr new_tun_factory(const DCO::TunConfig& conf, const OptionList& opt) override { @@ -89,6 +88,9 @@ namespace openvpn { // parse trunk-unit trunk_unit = opt.get_num("trunk-unit", 1, trunk_unit, 0, 511); + // parse ping-restart-override + ping_restart_override = opt.get_num("ping-restart-override", 1, ping_restart_override, 0, 3600); + return TunClientFactory::Ptr(this); } @@ -195,7 +197,7 @@ namespace openvpn { devconf.dc.peer_lookup = OVPN_PEER_LOOKUP_NONE; devconf.dc.cpu_id = -1; - // create kovpn tun socket + // create kovpn tun socket (implementation in kodevtun.hpp) impl.reset(new TunImpl(io_context, devconf, this, @@ -216,12 +218,6 @@ namespace openvpn { transport_start_udp(); } - // VPN IP collision detection for multi-channel trunking - static void set_vpn_ip_collision(IPCollisionDetectBase* vpn_ip_collision_arg) - { - vpn_ip_collision = vpn_ip_collision_arg; - } - virtual bool transport_send_const(const Buffer& buf) override { return send(buf); @@ -323,47 +319,55 @@ namespace openvpn { OPENVPN_LOG("CAPTURED OPTIONS:" << std::endl << po->to_string()); - // add/remove command lists - ActionList::Ptr add_cmds = new ActionList(); - remove_cmds.reset(new ActionListReversed()); - - // configure tun properties - std::vector rtvec; +#ifdef ENABLE_PG if (config->trunk_unit >= 0) { - // VPN IP collision detection, will throw on collision - if (vpn_ip_collision) - detect_vpn_ip_collision(*vpn_ip_collision, *po, config->trunk_unit, *remove_cmds); - // trunk setup - TunIPRoute::iface_config(state->iface_name, - config->trunk_unit, - *po, - nullptr, - *add_cmds, - *remove_cmds); + ovpn_peer_assign_route_id kri; + std::memset(&kri, 0, sizeof(kri)); + kri.peer_id = peer_id; + kri.route_id = config->trunk_unit; + kri.allow_incoming = true; + kri.snat_flags = OVPN_SNAT_DEFAULT_ON | OVPN_SNAT_REQUIRED; - // Note that in trunking mode, kovpn must be - // configured for source routing. - add_vpn_ips_as_source_routes(*po, rtvec, IP::Addr::V4); - add_vpn_ips_as_source_routes(*po, rtvec, IP::Addr::V6); + // SNAT via VPN IPv4 addresses received from server + { + const TunBuilderCapture::RouteAddress *ra = po->vpn_ip(IP::Addr::V4); + if (ra) + kri.snat.a4 = IP::Addr(ra->address, "server-assigned-vpn4-addr", IP::Addr::V4).to_ipv4().to_in_addr(); + } + + // SNAT via VPN IPv6 addresses received from server + { + const TunBuilderCapture::RouteAddress *ra = po->vpn_ip(IP::Addr::V6); + if (ra) + kri.snat.a6 = IP::Addr(ra->address, "server-assigned-vpn6-addr", IP::Addr::V4).to_ipv6().to_in6_addr(); + } + + // kovpn route ID setup + KoTun::API::peer_assign_route_id(impl->native_handle(), &kri); } else +#endif // ENABLE_PG { + // add/remove command lists + ActionList::Ptr add_cmds = new ActionList(); + remove_cmds.reset(new ActionListReversed()); + + // configure tun properties + std::vector rtvec; + // non-trunk setup - TunIPRoute::TunMethods::tun_config(state->iface_name, - *po, - &rtvec, - *add_cmds, - *remove_cmds); + TUN_LINUX::tun_config(state->iface_name, *po, &rtvec, *add_cmds, + *remove_cmds); + + // Add routes to DCO implementation + impl->peer_add_routes(peer_id, rtvec); + + // execute commands to bring up interface + add_cmds->execute_log(); } - // Add routes to DCO implementation - impl->peer_add_routes(peer_id, rtvec); - - // execute commands to bring up interface - add_cmds->execute_log(); - // Add a hook so ProtoContext will call back to // rekey() on rekey ops. dc_settings.set_factory(CryptoDCFactory::Ptr(new KoRekey::Factory(dc_settings.factory(), this, config->transport.frame))); @@ -371,12 +375,6 @@ namespace openvpn { // signal that we are connected tun_parent->tun_connected(); } - catch (const IPCollisionDetectBase::ip_collision& e) - { - // on VPN IP address collision, just reconnect to get a new address - stop_(); - tun_parent->tun_error(Error::TUN_ERROR, e.what()); - } catch (const std::exception& e) { stop_(); @@ -453,6 +451,10 @@ namespace openvpn { transport_parent->disable_keepalive(ka.keepalive_ping, ka.keepalive_timeout); + // Allow overide of keepalive timeout + if (config->ping_restart_override) + ka.keepalive_timeout = config->ping_restart_override; + // Modify the peer impl->peer_set_keepalive(&ka); } @@ -659,35 +661,6 @@ namespace openvpn { return *static_cast(proto.get()); } - static void add_vpn_ips_as_source_routes(const TunBuilderCapture& pull, - std::vector& rtvec, - const IP::Addr::Version ver) - { - const TunBuilderCapture::RouteAddress *ra = pull.vpn_ip(ver); - if (ra) - rtvec.push_back(IP::route_from_string_prefix(ra->address, - IP::Addr::version_size(ver), - "DCOTransport::Client::add_vpn_ips_as_source_routes", - ver)); - } - - // Throw an exception of type IPCollisionDetectBase::ip_collision - // if VPN IP is already in use by another client thread. - // This is intended to force a reconnect and obtain a - // new non-colliding address. - static void detect_vpn_ip_collision(IPCollisionDetectBase& ipcoll, - const TunBuilderCapture& pull, - unsigned int unit, - ActionList& remove) - { - const TunBuilderCapture::RouteAddress* local4 = pull.vpn_ipv4(); - const TunBuilderCapture::RouteAddress* local6 = pull.vpn_ipv6(); - if (local4) - ipcoll.add(local4->address, unit, remove); - if (local6) - ipcoll.add(local6->address, unit, remove); - } - // override for SessionStats::DCOTransportSource virtual SessionStats::DCOTransportSource::Data dco_transport_stats_delta() override { @@ -727,8 +700,6 @@ namespace openvpn { SessionStats::DCOTransportSource::Data last_stats; __u64 cc_rx_bytes = 0; - - static IPCollisionDetectBase* vpn_ip_collision; }; inline DCO::Ptr new_controller() @@ -750,8 +721,6 @@ namespace openvpn { cli->tun_parent = &parent; return TunClient::Ptr(cli); } - - IPCollisionDetectBase* Client::vpn_ip_collision; // GLOBAL } }; diff --git a/openvpn/error/error.hpp b/openvpn/error/error.hpp index 9ebd9c1..59cc61e 100644 --- a/openvpn/error/error.hpp +++ b/openvpn/error/error.hpp @@ -51,6 +51,7 @@ namespace openvpn { TUN_IFACE_CREATE, // error creating tun/tap interface TUN_IFACE_DISABLED, // tun/tap interface is disabled TUN_ERROR, // general tun error + TUN_REGISTER_RINGS_ERROR, // error registering ring buffers with wintun TAP_NOT_SUPPORTED, // dev tap is present in profile but not supported REROUTE_GW_NO_DNS, // redirect-gateway specified without alt DNS servers TRANSPORT_ERROR, // general transport error @@ -76,6 +77,7 @@ namespace openvpn { AUTH_FAILED, // general authentication failure CLIENT_HALT, // HALT message from server received CLIENT_RESTART, // RESTART message from server received + TUN_HALT, // halt command from tun interface RELAY, // RELAY message from server received RELAY_ERROR, // RELAY error N_PAUSE, // Number of transitions to Pause state @@ -127,6 +129,7 @@ namespace openvpn { "TUN_IFACE_CREATE", "TUN_IFACE_DISABLED", "TUN_ERROR", + "TUN_REGISTER_RINGS_ERROR", "TAP_NOT_SUPPORTED", "REROUTE_GW_NO_DNS", "TRANSPORT_ERROR", @@ -152,6 +155,7 @@ namespace openvpn { "AUTH_FAILED", "CLIENT_HALT", "CLIENT_RESTART", + "TUN_HALT", "RELAY", "RELAY_ERROR", "N_PAUSE", diff --git a/openvpn/frame/frame.hpp b/openvpn/frame/frame.hpp index 74d5d97..64b1909 100644 --- a/openvpn/frame/frame.hpp +++ b/openvpn/frame/frame.hpp @@ -46,7 +46,6 @@ namespace openvpn { READ_LINK_UDP, READ_LINK_TCP, READ_TUN, - READ_WINTUN, READ_BIO_MEMQ_DGRAM, READ_BIO_MEMQ_STREAM, READ_SSL_CLEARTEXT, diff --git a/openvpn/frame/frame_init.hpp b/openvpn/frame/frame_init.hpp index adc75ea..c34cd3a 100644 --- a/openvpn/frame/frame_init.hpp +++ b/openvpn/frame/frame_init.hpp @@ -52,12 +52,6 @@ namespace openvpn { (*frame)[Frame::WRITE_SSL_CLEARTEXT] = Frame::Context(headroom, payload, tailroom, 0, align_block, BufferAllocated::GROW); frame->standardize_capacity(~0); - // Wintun could return 256 packets no larger than tun mtu, - // each packet is prepended with 4 bytes size and 12 bytes - // start padding and appended by up to 15 bytes end padding - const size_t tun_mtu_wintun = tun_mtu > 0 ? tun_mtu : 1500; - (*frame)[Frame::READ_WINTUN] = Frame::Context(0, (tun_mtu_wintun + 4 + 12 + 15) * 256, 0, 0, align_block, 0); - if (verbose) OPENVPN_LOG("Frame=" << headroom << '/' << payload << '/' << tailroom << " mssfix-ctrl=" << (*frame)[Frame::READ_BIO_MEMQ_STREAM].payload()); diff --git a/openvpn/ip/icmp6.hpp b/openvpn/ip/icmp6.hpp index 2813004..647849b 100644 --- a/openvpn/ip/icmp6.hpp +++ b/openvpn/ip/icmp6.hpp @@ -36,6 +36,7 @@ namespace openvpn { enum { ECHO_REQUEST = 128, ECHO_REPLY = 129, + DEST_UNREACH = 1, PACKET_TOO_BIG = 2 }; diff --git a/openvpn/mbedtls/pki/pkctx.hpp b/openvpn/mbedtls/pki/pkctx.hpp index 3f2158f..5b37dc9 100644 --- a/openvpn/mbedtls/pki/pkctx.hpp +++ b/openvpn/mbedtls/pki/pkctx.hpp @@ -63,23 +63,23 @@ namespace openvpn { return ctx != nullptr; } - SSLConfigAPI::PKType key_type() const + PKType::Type key_type() const { switch (mbedtls_pk_get_type(ctx)) { case MBEDTLS_PK_RSA: case MBEDTLS_PK_RSA_ALT: case MBEDTLS_PK_RSASSA_PSS: - return SSLConfigAPI::PK_RSA; + return PKType::PK_RSA; case MBEDTLS_PK_ECKEY: case MBEDTLS_PK_ECKEY_DH: - return SSLConfigAPI::PK_EC; + return PKType::PK_EC; case MBEDTLS_PK_ECDSA: - return SSLConfigAPI::PK_ECDSA; + return PKType::PK_ECDSA; case MBEDTLS_PK_NONE: - return SSLConfigAPI::PK_NONE; + return PKType::PK_NONE; default: - return SSLConfigAPI::PK_UNKNOWN; + return PKType::PK_UNKNOWN; } } diff --git a/openvpn/mbedtls/ssl/sslctx.hpp b/openvpn/mbedtls/ssl/sslctx.hpp index 051df51..ecb71c6 100644 --- a/openvpn/mbedtls/ssl/sslctx.hpp +++ b/openvpn/mbedtls/ssl/sslctx.hpp @@ -248,6 +248,18 @@ namespace openvpn { throw MbedTLSException("set_client_session_tickets not implemented"); } + virtual void set_sni_handler(SNI::HandlerBase* sni_handler) + { + // fixme -- this method should be implemented on the server-side for SNI + throw MbedTLSException("set_sni_handler not implemented"); + } + + virtual void set_sni_name(const std::string& sni_name_arg) + { + // fixme -- this method should be implemented on the client-side for SNI + throw MbedTLSException("set_sni_name not implemented"); + } + virtual void set_private_key_password(const std::string& pwd) { priv_key_pwd = pwd; @@ -339,10 +351,10 @@ namespace openvpn { return dh->extract(); } - virtual PKType private_key_type() const + virtual PKType::Type private_key_type() const { if (!priv_key) - return PK_NONE; + return PKType::PK_NONE; return priv_key->key_type(); } @@ -467,6 +479,13 @@ namespace openvpn { allow_name_constraints = lflags & LF_ALLOW_NAME_CONSTRAINTS; + // sni + { + const std::string name = opt.get_optional("sni", 1, 256); + if (!name.empty()) + set_sni_name(name); + } + // ca { std::string ca_txt = opt.cat("ca"); @@ -543,6 +562,13 @@ namespace openvpn { } } +#ifdef HAVE_JSON + virtual SSLConfigAPI::Ptr json_override(const Json::Value& root, const bool load_cert_key) const + { + throw MbedTLSException("json_override not implemented"); + } +#endif + bool name_constraints_allowed() const { return allow_name_constraints; @@ -709,7 +735,7 @@ namespace openvpn { return false; // fixme -- not implemented } - virtual const AuthCert::Ptr& auth_cert() override + virtual const AuthCert::Ptr& auth_cert() const override { return authcert; } @@ -791,11 +817,23 @@ namespace openvpn { #endif } - // peer must present a valid certificate unless SSLConst::NO_VERIFY_PEER is set - mbedtls_ssl_conf_authmode(sslconf, - (c.flags & SSLConst::NO_VERIFY_PEER) - ? MBEDTLS_SSL_VERIFY_NONE - : MBEDTLS_SSL_VERIFY_REQUIRED); + + { + // peer must present a valid certificate unless SSLConst::NO_VERIFY_PEER. + // Presenting a valid certificate can be made optional by specifying + // SSL:Const::PEER_CERT_OPTIONAL + + int authmode; + + if (c.flags & SSLConst::NO_VERIFY_PEER) + authmode = MBEDTLS_SSL_VERIFY_NONE; + else if (c.flags & SSLConst::PEER_CERT_OPTIONAL) + throw MbedTLSException("Optional peer verification not supported"); + else + authmode = MBEDTLS_SSL_VERIFY_REQUIRED; + + mbedtls_ssl_conf_authmode(sslconf, authmode); + } // set verify callback mbedtls_ssl_conf_verify(sslconf, c.mode.is_server() ? verify_callback_server : verify_callback_client, this); @@ -825,7 +863,7 @@ namespace openvpn { // In pre-mbedtls-2.x the hostname for the CA chain was set in ssl_set_ca_chain(). // From mbedtls-2.x, the hostname must be set via mbedtls_ssl_set_hostname() // https://tls.mbed.org/kb/how-to/upgrade-2.0 - if (hostname && ((c.flags & SSLConst::ENABLE_SNI) || c.ca_chain)) + if (hostname && ((c.flags & SSLConst::ENABLE_CLIENT_SNI) || c.ca_chain)) { if (mbedtls_ssl_set_hostname(ssl, hostname)) throw MbedTLSException("mbedtls_ssl_set_hostname failed"); @@ -1423,6 +1461,16 @@ namespace openvpn { } }; + inline const std::string get_ssl_library_version() + { + unsigned int ver = mbedtls_version_get_number(); + std::string version = "mbed TLS " + + std::to_string((ver>>24)&0xff) + + "." + std::to_string((ver>>16)&0xff) + + "." + std::to_string((ver>>8)&0xff); + + return version; + } } // namespace openvpn #endif diff --git a/openvpn/mbedtls/util/pem.hpp b/openvpn/mbedtls/util/pem.hpp index e0524e6..f663fff 100644 --- a/openvpn/mbedtls/util/pem.hpp +++ b/openvpn/mbedtls/util/pem.hpp @@ -58,7 +58,7 @@ namespace openvpn { { std::string header = "-----BEGIN " + key_name + "-----"; std::string footer = "-----END " + key_name + "-----"; - mbedtls_pem_context ctx = { 0 }; + mbedtls_pem_context ctx = { }; size_t out_len = 0; int ret = mbedtls_pem_read_buffer(&ctx, header.c_str(), footer.c_str(), diff --git a/openvpn/openssl/compat.hpp b/openvpn/openssl/compat.hpp index bf2511a..7d8d995 100644 --- a/openvpn/openssl/compat.hpp +++ b/openvpn/openssl/compat.hpp @@ -97,7 +97,7 @@ inline EVP_MD_CTX *EVP_MD_CTX_new() return new EVP_MD_CTX(); } -void EVP_MD_CTX_free(EVP_MD_CTX *ctx) +inline void EVP_MD_CTX_free(EVP_MD_CTX *ctx) { delete ctx; } @@ -211,10 +211,10 @@ inline int RSA_meth_set_priv_enc(RSA_METHOD *meth, return 1; } -int RSA_meth_set_priv_dec(RSA_METHOD *meth, - int (*priv_dec)(int flen, const unsigned char *from, - unsigned char *to, RSA *rsa, - int padding)) +inline int RSA_meth_set_priv_dec(RSA_METHOD *meth, + int (*priv_dec)(int flen, const unsigned char *from, + unsigned char *to, RSA *rsa, + int padding)) { meth->rsa_priv_dec = priv_dec; return 1; @@ -307,6 +307,19 @@ inline void RSA_get0_key(const RSA *rsa, const BIGNUM **n, const BIGNUM **e, con /* Renamed in OpenSSL 1.1 */ #define X509_get0_pubkey X509_get_pubkey #define RSA_F_RSA_OSSL_PRIVATE_ENCRYPT RSA_F_RSA_EAY_PRIVATE_ENCRYPT + +/* + * EVP_CIPHER_CTX_init and EVP_CIPHER_CTX_cleanup are both replaced by + * EVP_CIPHER_CTX_reset in OpenSSL 1.1 but replacing them both with + * reset is wrong for older version. The man page mention cleanup + * being officially removed and init to be an alias for reset. + * + * So we only use reset as alias for init in older versions. + * + * EVP_CIPHER_CTX_free already implicitly calls EVP_CIPHER_CTX_cleanup in + * 1.0.2, so we can avoid using the old API. + */ +#define EVP_CIPHER_CTX_reset EVP_CIPHER_CTX_init #endif #if OPENSSL_VERSION_NUMBER < 0x10101000L @@ -333,4 +346,4 @@ inline const BIGNUM *DSA_get0_p(const DSA *d) DSA_get0_pqg(d, &p, nullptr, nullptr); return p; } -#endif \ No newline at end of file +#endif diff --git a/openvpn/openssl/crypto/cipher.hpp b/openvpn/openssl/crypto/cipher.hpp index 0b60952..c1b6f29 100644 --- a/openvpn/openssl/crypto/cipher.hpp +++ b/openvpn/openssl/crypto/cipher.hpp @@ -35,6 +35,7 @@ #include #include #include +#include namespace openvpn { namespace OpenSSLCrypto { @@ -75,7 +76,7 @@ namespace openvpn { throw openssl_cipher_mode_error(); erase(); ctx = EVP_CIPHER_CTX_new (); - EVP_CIPHER_CTX_init (ctx); + EVP_CIPHER_CTX_reset (ctx); if (!EVP_CipherInit_ex (ctx, cipher_type(alg), nullptr, key, nullptr, mode)) { openssl_clear_error_stack(); @@ -177,7 +178,6 @@ namespace openvpn { { if (initialized) { - EVP_CIPHER_CTX_cleanup(ctx); EVP_CIPHER_CTX_free (ctx); initialized = false; } diff --git a/openvpn/openssl/crypto/ciphergcm.hpp b/openvpn/openssl/crypto/ciphergcm.hpp index cabb136..4b5c923 100644 --- a/openvpn/openssl/crypto/ciphergcm.hpp +++ b/openvpn/openssl/crypto/ciphergcm.hpp @@ -78,7 +78,7 @@ namespace openvpn { if (ckeysz > keysize) throw openssl_gcm_error("insufficient key material"); ctx = EVP_CIPHER_CTX_new(); - EVP_CIPHER_CTX_init(ctx); + EVP_CIPHER_CTX_reset(ctx); switch (mode) { case ENCRYPT: @@ -223,15 +223,17 @@ namespace openvpn { { if (initialized) { - EVP_CIPHER_CTX_cleanup(ctx); + EVP_CIPHER_CTX_free(ctx); initialized = false; } } void check_initialized() const { +#ifdef OPENVPN_ENABLE_ASSERT if (unlikely(!initialized)) throw openssl_gcm_error("uninitialized"); +#endif } bool initialized; diff --git a/openvpn/openssl/pki/crl.hpp b/openvpn/openssl/pki/crl.hpp index c857e9f..4674a9e 100644 --- a/openvpn/openssl/pki/crl.hpp +++ b/openvpn/openssl/pki/crl.hpp @@ -21,8 +21,7 @@ // Wrap an OpenSSL X509_CRL object -#ifndef OPENVPN_OPENSSL_PKI_CRL_H -#define OPENVPN_OPENSSL_PKI_CRL_H +#pragma once #include #include @@ -32,16 +31,18 @@ #include #include -#include #include namespace openvpn { namespace OpenSSLPKI { - class CRL : public RC + class CRL { public: - CRL() : crl_(nullptr) {} + CRL() + : crl_(nullptr) + { + } explicit CRL(const std::string& crl_txt) : crl_(nullptr) @@ -50,27 +51,48 @@ namespace openvpn { } CRL(const CRL& other) - : crl_(nullptr) + : crl_(dup(other.crl_)) { - assign(other.crl_); } - void operator=(const CRL& other) + CRL(CRL&& other) noexcept + : crl_(other.crl_) { - assign(other.crl_); + other.crl_ = nullptr; + } + + CRL& operator=(const CRL& other) + { + if (this != &other) + { + erase(); + crl_ = dup(other.crl_); + } + return *this; + } + + CRL& operator=(CRL&& other) noexcept + { + if (this != &other) + { + erase(); + crl_ = other.crl_; + other.crl_ = nullptr; + } + return *this; } bool defined() const { return crl_ != nullptr; } - X509_CRL* obj() const { return crl_; } + ::X509_CRL* obj() const { return crl_; } void parse_pem(const std::string& crl_txt) { - BIO *bio = BIO_new_mem_buf(const_cast(crl_txt.c_str()), crl_txt.length()); + BIO *bio = ::BIO_new_mem_buf(const_cast(crl_txt.c_str()), crl_txt.length()); if (!bio) throw OpenSSLException(); - X509_CRL *crl = PEM_read_bio_X509_CRL(bio, nullptr, nullptr, nullptr); - BIO_free(bio); + ::X509_CRL *crl = ::PEM_read_bio_X509_CRL(bio, nullptr, nullptr, nullptr); + ::BIO_free(bio); if (!crl) throw OpenSSLException("CRL::parse_pem"); @@ -82,19 +104,19 @@ namespace openvpn { { if (crl_) { - BIO *bio = BIO_new(BIO_s_mem()); - const int ret = PEM_write_bio_X509_CRL(bio, crl_); + BIO *bio = ::BIO_new(BIO_s_mem()); + const int ret = ::PEM_write_bio_X509_CRL(bio, crl_); if (ret == 0) { - BIO_free(bio); + ::BIO_free(bio); throw OpenSSLException("CRL::render_pem"); } { char *temp; - const int buf_len = BIO_get_mem_data(bio, &temp); + const int buf_len = ::BIO_get_mem_data(bio, &temp); std::string ret = std::string(temp, buf_len); - BIO_free(bio); + ::BIO_free(bio); return ret; } } @@ -102,59 +124,46 @@ namespace openvpn { return ""; } - void erase() - { - if (crl_) - { - X509_CRL_free(crl_); - crl_ = nullptr; - } - } - ~CRL() { erase(); } private: + void erase() + { + if (crl_) + ::X509_CRL_free(crl_); + } + static X509_CRL *dup(const X509_CRL *crl) { if (crl) - { - return X509_CRL_dup(const_cast(crl)); - } + return ::X509_CRL_dup(const_cast(crl)); else return nullptr; } - void assign(const X509_CRL *crl) - { - erase(); - crl_ = dup(crl); - } - - X509_CRL *crl_; + ::X509_CRL *crl_; }; - typedef RCPtr CRLPtr; - - class CRLList : public std::vector + class CRLList : public std::vector { public: - typedef CRL Item; - typedef CRLPtr ItemPtr; + typedef X509 CRL; - bool defined() const { return !empty(); } + bool defined() const + { + return !empty(); + } std::string render_pem() const { std::string ret; - for (const_iterator i = begin(); i != end(); ++i) - ret += (*i)->render_pem(); + for (const auto &e : *this) + ret += e.render_pem(); return ret; } }; } -} // namespace openvpn - -#endif // OPENVPN_OPENSSL_PKI_CRL_H +} diff --git a/openvpn/openssl/pki/dh.hpp b/openvpn/openssl/pki/dh.hpp index baac178..d5a86f7 100644 --- a/openvpn/openssl/pki/dh.hpp +++ b/openvpn/openssl/pki/dh.hpp @@ -21,8 +21,7 @@ // Wrap an OpenSSL DH object -#ifndef OPENVPN_OPENSSL_PKI_DH_H -#define OPENVPN_OPENSSL_PKI_DH_H +#pragma once #include @@ -57,7 +56,10 @@ namespace openvpn { class DH { public: - DH() : dh_(nullptr) {} + DH() + : dh_(nullptr) + { + } explicit DH(const std::string& dh_txt) : dh_(nullptr) @@ -66,14 +68,34 @@ namespace openvpn { } DH(const DH& other) - : dh_(nullptr) { - assign(other.dh_); + dup(other.dh_); + } + + DH(DH&& other) noexcept + : dh_(other.dh_) + { + other.dh_ = nullptr; } void operator=(const DH& other) { - assign(other.dh_); + if (this != &other) + { + erase(); + dup(other.dh_); + } + } + + DH& operator=(DH&& other) noexcept + { + if (this != &other) + { + erase(); + dh_ = other.dh_; + other.dh_ = nullptr; + } + return *this; } bool defined() const { return dh_ != nullptr; } @@ -81,12 +103,12 @@ namespace openvpn { void parse_pem(const std::string& dh_txt) { - BIO *bio = BIO_new_mem_buf(const_cast(dh_txt.c_str()), dh_txt.length()); + BIO *bio = ::BIO_new_mem_buf(const_cast(dh_txt.c_str()), dh_txt.length()); if (!bio) throw OpenSSLException(); - ::DH *dh = PEM_read_bio_DHparams(bio, nullptr, nullptr, nullptr); - BIO_free(bio); + ::DH *dh = ::PEM_read_bio_DHparams(bio, nullptr, nullptr, nullptr); + ::BIO_free(bio); if (!dh) throw OpenSSLException("DH::parse_pem"); @@ -98,19 +120,19 @@ namespace openvpn { { if (dh_) { - BIO *bio = BIO_new(BIO_s_mem()); - const int ret = PEM_write_bio_DHparams(bio, dh_); + BIO *bio = ::BIO_new(BIO_s_mem()); + const int ret = ::PEM_write_bio_DHparams(bio, dh_); if (ret == 0) { - BIO_free(bio); + ::BIO_free(bio); throw OpenSSLException("DH::render_pem"); } { char *temp; - const int buf_len = BIO_get_mem_data(bio, &temp); + const int buf_len = ::BIO_get_mem_data(bio, &temp); std::string ret = std::string(temp, buf_len); - BIO_free(bio); + ::BIO_free(bio); return ret; } } @@ -118,31 +140,24 @@ namespace openvpn { return ""; } - void erase() - { - if (dh_) - { - DH_free(dh_); - dh_ = nullptr; - } - } - ~DH() { erase(); } private: - void assign(const ::DH *dh) + void erase() + { + if (dh_) + ::DH_free(dh_); + } + + void dup(const ::DH *dh) { - erase(); dh_ = DH_private::dup(dh); } ::DH *dh_; }; } -} // namespace openvpn - -#endif // OPENVPN_OPENSSL_PKI_DH_H - +} diff --git a/openvpn/openssl/pki/pkey.hpp b/openvpn/openssl/pki/pkey.hpp index 2f1324a..07e82db 100644 --- a/openvpn/openssl/pki/pkey.hpp +++ b/openvpn/openssl/pki/pkey.hpp @@ -21,10 +21,10 @@ // Wrap an OpenSSL EVP_PKEY object -#ifndef OPENVPN_OPENSSL_PKI_PKEY_H -#define OPENVPN_OPENSSL_PKI_PKEY_H +#pragma once #include +#include #include #include @@ -32,6 +32,7 @@ #include #include #include +#include namespace openvpn { namespace OpenSSLPKI { @@ -39,7 +40,10 @@ namespace openvpn { class PKey { public: - PKey() : pkey_(nullptr) {} + PKey() + : pkey_(nullptr) + { + } PKey(const std::string& pkey_txt, const std::string& title) : pkey_(nullptr) @@ -48,45 +52,69 @@ namespace openvpn { } PKey(const PKey& other) - : pkey_(nullptr) + : pkey_(dup(other.pkey_)), + priv_key_pwd(other.priv_key_pwd) { - assign(other.pkey_); } - void operator=(const PKey& other) + PKey(PKey&& other) noexcept + : pkey_(other.pkey_), + priv_key_pwd(std::move(other.priv_key_pwd)) { - assign(other.pkey_); - priv_key_pwd = other.priv_key_pwd; + other.pkey_ = nullptr; + } + + PKey& operator=(const PKey& other) + { + if (this != &other) + { + erase(); + pkey_ = dup(other.pkey_); + priv_key_pwd = other.priv_key_pwd; + } + return *this; + } + + PKey& operator=(PKey&& other) noexcept + { + if (this != &other) + { + erase(); + pkey_ = other.pkey_; + other.pkey_ = nullptr; + priv_key_pwd = std::move(other.priv_key_pwd); + } + return *this; } bool defined() const { return pkey_ != nullptr; } - EVP_PKEY* obj() const { return pkey_; } + ::EVP_PKEY* obj() const { return pkey_; } - SSLConfigAPI::PKType key_type() const + PKType::Type key_type() const { - switch (EVP_PKEY_id(pkey_)) + switch (::EVP_PKEY_id(pkey_)) { case EVP_PKEY_RSA: case EVP_PKEY_RSA2: - return SSLConfigAPI::PK_RSA; + return PKType::PK_RSA; case EVP_PKEY_EC: - return SSLConfigAPI::PK_EC; + return PKType::PK_EC; case EVP_PKEY_DSA: case EVP_PKEY_DSA1: case EVP_PKEY_DSA2: case EVP_PKEY_DSA3: case EVP_PKEY_DSA4: - return SSLConfigAPI::PK_DSA; + return PKType::PK_DSA; case EVP_PKEY_NONE: - return SSLConfigAPI::PK_NONE; + return PKType::PK_NONE; default: - return SSLConfigAPI::PK_UNKNOWN; + return PKType::PK_UNKNOWN; } } size_t key_length() const { - int ret = i2d_PrivateKey(pkey_, NULL); + int ret = ::i2d_PrivateKey(pkey_, NULL); if (ret < 0) return 0; @@ -101,12 +129,12 @@ namespace openvpn { void parse_pem(const std::string& pkey_txt, const std::string& title) { - BIO *bio = BIO_new_mem_buf(const_cast(pkey_txt.c_str()), pkey_txt.length()); + BIO *bio = ::BIO_new_mem_buf(const_cast(pkey_txt.c_str()), pkey_txt.length()); if (!bio) throw OpenSSLException(); - EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bio, nullptr, pem_password_callback, this); - BIO_free(bio); + ::EVP_PKEY *pkey = ::PEM_read_bio_PrivateKey(bio, nullptr, pem_password_callback, this); + ::BIO_free(bio); if (!pkey) throw OpenSSLException(std::string("PKey::parse_pem: error in ") + title + std::string(":")); @@ -118,19 +146,19 @@ namespace openvpn { { if (pkey_) { - BIO *bio = BIO_new(BIO_s_mem()); - const int ret = PEM_write_bio_PrivateKey(bio, pkey_, nullptr, nullptr, 0, nullptr, nullptr); + BIO *bio = ::BIO_new(BIO_s_mem()); + const int ret = ::PEM_write_bio_PrivateKey(bio, pkey_, nullptr, nullptr, 0, nullptr, nullptr); if (ret == 0) { - BIO_free(bio); + ::BIO_free(bio); throw OpenSSLException("PKey::render_pem"); } { char *temp; - const int buf_len = BIO_get_mem_data(bio, &temp); + const int buf_len = ::BIO_get_mem_data(bio, &temp); std::string ret = std::string(temp, buf_len); - BIO_free(bio); + ::BIO_free(bio); return ret; } } @@ -138,15 +166,6 @@ namespace openvpn { return ""; } - void erase() - { - if (pkey_) - { - EVP_PKEY_free(pkey_); - pkey_ = nullptr; - } - } - ~PKey() { erase(); @@ -165,33 +184,31 @@ namespace openvpn { return 0; } - static EVP_PKEY *dup(const EVP_PKEY *pkey) + void erase() + { + if (pkey_) + ::EVP_PKEY_free(pkey_); + } + + static ::EVP_PKEY *dup(const ::EVP_PKEY *pkey) { // No OpenSSL EVP_PKEY_dup method so we roll our own if (pkey) { - EVP_PKEY* pDupKey = EVP_PKEY_new(); - RSA* pRSA = EVP_PKEY_get1_RSA(const_cast(pkey)); - RSA* pRSADupKey = RSAPrivateKey_dup(pRSA); - RSA_free(pRSA); - EVP_PKEY_set1_RSA(pDupKey, pRSADupKey); - RSA_free(pRSADupKey); + ::EVP_PKEY* pDupKey = ::EVP_PKEY_new(); + ::RSA* pRSA = ::EVP_PKEY_get1_RSA(const_cast<::EVP_PKEY *>(pkey)); + ::RSA* pRSADupKey = ::RSAPrivateKey_dup(pRSA); + ::RSA_free(pRSA); + ::EVP_PKEY_set1_RSA(pDupKey, pRSADupKey); + ::RSA_free(pRSADupKey); return pDupKey; } else return nullptr; } - void assign(const EVP_PKEY *pkey) - { - erase(); - pkey_ = dup(pkey); - } - + ::EVP_PKEY *pkey_; std::string priv_key_pwd; - EVP_PKEY *pkey_; }; } -} // namespace openvpn - -#endif // OPENVPN_OPENSSL_PKI_PKEY_H +} diff --git a/openvpn/openssl/pki/x509.hpp b/openvpn/openssl/pki/x509.hpp index f3a6daf..dfa6401 100644 --- a/openvpn/openssl/pki/x509.hpp +++ b/openvpn/openssl/pki/x509.hpp @@ -21,8 +21,7 @@ // Wrap an OpenSSL X509 object -#ifndef OPENVPN_OPENSSL_PKI_X509_H -#define OPENVPN_OPENSSL_PKI_X509_H +#pragma once #include #include @@ -32,89 +31,77 @@ #include #include -#include #include namespace openvpn { namespace OpenSSLPKI { - class X509; - - class X509Base + class X509 { public: - X509Base() : x509_(nullptr) {} - explicit X509Base(::X509 *x509) : x509_(x509) {} + X509() + : x509_(nullptr) + { + } + + X509(const std::string& cert_txt, const std::string& title) + : x509_(nullptr) + { + parse_pem(cert_txt, title); + } + + explicit X509(::X509 *x509, const bool create=true) + { + if (create) + x509_ = x509; + else + x509_ = dup(x509); + } + + X509(const X509& other) + : x509_(dup(other.x509_)) + { + } + + X509(X509&& other) noexcept + : x509_(other.x509_) + { + other.x509_ = nullptr; + } + + X509& operator=(const X509& other) + { + if (this != &other) + { + erase(); + x509_ = dup(other.x509_); + } + return *this; + } + + X509& operator=(X509&& other) noexcept + { + if (this != &other) + { + erase(); + x509_ = other.x509_; + other.x509_ = nullptr; + } + return *this; + } bool defined() const { return x509_ != nullptr; } ::X509* obj() const { return x509_; } ::X509* obj_dup() const { return dup(x509_); } - std::string render_pem() const - { - if (x509_) - { - BIO *bio = BIO_new(BIO_s_mem()); - const int ret = PEM_write_bio_X509(bio, x509_); - if (ret == 0) - { - BIO_free(bio); - throw OpenSSLException("X509::render_pem"); - } - - { - char *temp; - const int buf_len = BIO_get_mem_data(bio, &temp); - std::string ret = std::string(temp, buf_len); - BIO_free(bio); - return ret; - } - } - else - return ""; - } - - private: - static ::X509 *dup(const ::X509 *x509) - { - if (x509) - return X509_dup(const_cast< ::X509 * >(x509)); - else - return nullptr; - } - - friend class X509; - ::X509 *x509_; - }; - - class X509 : public X509Base, public RC - { - public: - X509() {} - - X509(const std::string& cert_txt, const std::string& title) - { - parse_pem(cert_txt, title); - } - - X509(const X509& other) - { - assign(other.x509_); - } - - void operator=(const X509& other) - { - assign(other.x509_); - } - void parse_pem(const std::string& cert_txt, const std::string& title) { - BIO *bio = BIO_new_mem_buf(const_cast(cert_txt.c_str()), cert_txt.length()); + BIO *bio = ::BIO_new_mem_buf(const_cast(cert_txt.c_str()), cert_txt.length()); if (!bio) throw OpenSSLException(); - ::X509 *cert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr); - BIO_free(bio); + ::X509 *cert = ::PEM_read_bio_X509(bio, nullptr, nullptr, nullptr); + ::BIO_free(bio); if (!cert) throw OpenSSLException(std::string("X509::parse_pem: error in ") + title + std::string(":")); @@ -122,13 +109,28 @@ namespace openvpn { x509_ = cert; } - void erase() + std::string render_pem() const { if (x509_) { - X509_free(x509_); - x509_ = nullptr; + BIO *bio = ::BIO_new(BIO_s_mem()); + const int ret = ::PEM_write_bio_X509(bio, x509_); + if (ret == 0) + { + ::BIO_free(bio); + throw OpenSSLException("X509::render_pem"); + } + + { + char *temp; + const int buf_len = ::BIO_get_mem_data(bio, &temp); + std::string ret = std::string(temp, buf_len); + ::BIO_free(bio); + return ret; + } } + else + return ""; } ~X509() @@ -137,32 +139,40 @@ namespace openvpn { } private: - void assign(const ::X509 *x509) + static ::X509 *dup(const ::X509 *x509) { - erase(); - x509_ = dup(x509); + if (x509) + return ::X509_dup(const_cast< ::X509 * >(x509)); + else + return nullptr; } + + void erase() + { + if (x509_) + ::X509_free(x509_); + } + + ::X509 *x509_; }; - typedef RCPtr X509Ptr; - - class X509List : public std::vector + class X509List : public std::vector { public: typedef X509 Item; - typedef X509Ptr ItemPtr; - bool defined() const { return !empty(); } + bool defined() const + { + return !empty(); + } std::string render_pem() const { std::string ret; - for (const_iterator i = begin(); i != end(); ++i) - ret += (*i)->render_pem(); + for (const auto &e : *this) + ret += e.render_pem(); return ret; } }; } -} // namespace openvpn - -#endif // OPENVPN_OPENSSL_PKI_X509_H +} diff --git a/openvpn/openssl/pki/x509store.hpp b/openvpn/openssl/pki/x509store.hpp index fb475cd..2aee345 100644 --- a/openvpn/openssl/pki/x509store.hpp +++ b/openvpn/openssl/pki/x509store.hpp @@ -21,12 +21,10 @@ // Wrap an OpenSSL X509Store object -#ifndef OPENVPN_OPENSSL_PKI_X509STORE_H -#define OPENVPN_OPENSSL_PKI_X509STORE_H +#pragma once #include #include -#include #include #include #include @@ -35,16 +33,17 @@ namespace openvpn { namespace OpenSSLPKI { - class X509Store : public RC + class X509Store { public: - OPENVPN_SIMPLE_EXCEPTION(x509_store_init_error); - OPENVPN_SIMPLE_EXCEPTION(x509_store_add_cert_error); - OPENVPN_SIMPLE_EXCEPTION(x509_store_add_crl_error); + OPENVPN_EXCEPTION(x509_store_error); typedef CertCRLListTemplate CertCRLList; - X509Store() : x509_store_(nullptr) {} + X509Store() + : x509_store_(nullptr) + { + } explicit X509Store(const CertCRLList& cc) { @@ -52,10 +51,10 @@ namespace openvpn { // Load cert list { - for (X509List::const_iterator i = cc.certs.begin(); i != cc.certs.end(); ++i) + for (const auto &e : cc.certs) { - if (!X509_STORE_add_cert(x509_store_, (*i)->obj())) - throw x509_store_add_cert_error(); + if (!::X509_STORE_add_cert(x509_store_, e.obj())) + throw x509_store_error("X509_STORE_add_cert("); } } @@ -63,19 +62,22 @@ namespace openvpn { { if (cc.crls.defined()) { - X509_STORE_set_flags(x509_store_, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); - for (CRLList::const_iterator i = cc.crls.begin(); i != cc.crls.end(); ++i) + ::X509_STORE_set_flags(x509_store_, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); + for (const auto &e : cc.crls) { - if (!X509_STORE_add_crl(x509_store_, (*i)->obj())) - throw x509_store_add_crl_error(); + if (!::X509_STORE_add_crl(x509_store_, e.obj())) + throw x509_store_error("X509_STORE_add_crl"); } } } } - X509_STORE* obj() const { return x509_store_; } + X509_STORE* obj() const + { + return x509_store_; + } - X509_STORE* move() + X509_STORE* release() { X509_STORE* ret = x509_store_; x509_store_ = nullptr; @@ -85,20 +87,18 @@ namespace openvpn { ~X509Store() { if (x509_store_) - X509_STORE_free(x509_store_); + ::X509_STORE_free(x509_store_); } private: void init() { - x509_store_ = X509_STORE_new(); + x509_store_ = ::X509_STORE_new(); if (!x509_store_) - throw x509_store_init_error(); + throw x509_store_error("X509_STORE_new"); } - X509_STORE* x509_store_; + ::X509_STORE* x509_store_; }; } -} // namespace openvpn - -#endif // OPENVPN_OPENSSL_PKI_X509STORE_H +} diff --git a/openvpn/openssl/sign/pkcs7verify.hpp b/openvpn/openssl/sign/pkcs7verify.hpp index 31fc9b2..888f48b 100644 --- a/openvpn/openssl/sign/pkcs7verify.hpp +++ b/openvpn/openssl/sign/pkcs7verify.hpp @@ -41,7 +41,7 @@ namespace openvpn { * On success, return. * On fail, throw exception. */ - inline void verify_pkcs7(const OpenSSLPKI::X509Base& cert, + inline void verify_pkcs7(const OpenSSLPKI::X509& cert, const std::string& sig, const std::string& data) { diff --git a/openvpn/openssl/sign/verify.hpp b/openvpn/openssl/sign/verify.hpp index be62fb5..2dd406b 100644 --- a/openvpn/openssl/sign/verify.hpp +++ b/openvpn/openssl/sign/verify.hpp @@ -34,6 +34,8 @@ #include #include +#include + namespace openvpn { namespace OpenSSLSign { /* @@ -41,21 +43,25 @@ namespace openvpn { * On success, return. * On fail, throw exception. */ - inline void verify(const OpenSSLPKI::X509Base& cert, + inline void verify(const OpenSSLPKI::X509& cert, const std::string& sig, const std::string& data, const std::string& digest) { const EVP_MD *dig; - EVP_MD_CTX md_ctx; - bool md_ctx_initialized = false; + EVP_MD_CTX* md_ctx = nullptr; EVP_PKEY *pkey = nullptr; auto clean = Cleanup([&]() { if (pkey) EVP_PKEY_free(pkey); - if (md_ctx_initialized) - EVP_MD_CTX_cleanup(&md_ctx); + if (md_ctx) + { +#if OPENSSL_VERSION_NUMBER < 0x10100000L + EVP_MD_CTX_cleanup(md_ctx); +#endif + EVP_MD_CTX_free(md_ctx); + } }); // get digest @@ -78,11 +84,13 @@ namespace openvpn { throw Exception(std::string("OpenSSLSign::verify: base64 decode error on signature: ") + e.what()); } + // initialize digest context + md_ctx = EVP_MD_CTX_new(); + // verify signature - EVP_VerifyInit (&md_ctx, dig); - md_ctx_initialized = 1; - EVP_VerifyUpdate(&md_ctx, data.c_str(), data.length()); - if (EVP_VerifyFinal(&md_ctx, binsig.c_data(), binsig.length(), pkey) != 1) + EVP_VerifyInit (md_ctx, dig); + EVP_VerifyUpdate(md_ctx, data.c_str(), data.length()); + if (EVP_VerifyFinal(md_ctx, binsig.c_data(), binsig.length(), pkey) != 1) throw OpenSSLException("OpenSSLSign::verify: verification failed"); } } diff --git a/openvpn/openssl/ssl/sslctx.hpp b/openvpn/openssl/ssl/sslctx.hpp index adf1db7..aac5ff1 100644 --- a/openvpn/openssl/ssl/sslctx.hpp +++ b/openvpn/openssl/ssl/sslctx.hpp @@ -33,6 +33,8 @@ #include #include #include +#include +#include #include #include @@ -44,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -55,6 +58,7 @@ #include #include #include +#include #include #include #include @@ -64,6 +68,10 @@ #include #include +#ifdef HAVE_JSON +#include +#endif + // An SSL Context is essentially a configuration that can be used // to generate an arbitrary number of actual SSL connections objects. @@ -125,6 +133,18 @@ namespace openvpn { client_session_tickets = v; } + // server side + virtual void set_sni_handler(SNI::HandlerBase* sni_handler_arg) + { + sni_handler = sni_handler_arg; + } + + // client side + virtual void set_sni_name(const std::string& sni_name_arg) + { + sni_name = sni_name_arg; + } + virtual void set_private_key_password(const std::string& pwd) { pkey.set_private_key_password(pwd); @@ -182,7 +202,7 @@ namespace openvpn { std::vector ret; for (auto const& cert : extra_certs) - ret.push_back(cert->render_pem()); + ret.push_back(cert.render_pem()); return ret; } @@ -197,10 +217,10 @@ namespace openvpn { return dh.render_pem(); } - virtual PKType private_key_type() const + virtual PKType::Type private_key_type() const { if (!pkey.defined()) - return PK_NONE; + return PKType::PK_NONE; return pkey.key_type(); } @@ -322,6 +342,13 @@ namespace openvpn { && opt.exists("client-cert-not-required")) flags |= SSLConst::NO_VERIFY_PEER; + // sni + { + const std::string name = opt.get_optional("sni", 1, 256); + if (!name.empty()) + set_sni_name(name); + } + // ca { std::string ca_txt = opt.cat("ca"); @@ -379,18 +406,7 @@ namespace openvpn { tls_remote = opt.get_optional(relay_prefix + "tls-remote", 1, 256); // Parse tls-version-min option. - // Assume that presence of SSL_OP_NO_TLSvX macro indicates - // that local OpenSSL library implements TLSvX. - { -# if defined(SSL_OP_NO_TLSv1_2) - const TLSVersion::Type maxver = TLSVersion::V1_2; -# elif defined(SSL_OP_NO_TLSv1_1) - const TLSVersion::Type maxver = TLSVersion::V1_1; -# else - const TLSVersion::Type maxver = TLSVersion::V1_0; -# endif - tls_version_min = TLSVersion::parse_tls_version_min(opt, relay_prefix, maxver); - } + tls_version_min = TLSVersion::parse_tls_version_min(opt, relay_prefix, maxver()); // parse tls-cert-profile tls_cert_profile = TLSCertProfile::parse_tls_cert_profile(opt, relay_prefix); @@ -400,7 +416,122 @@ namespace openvpn { } } +#ifdef HAVE_JSON + virtual SSLConfigAPI::Ptr json_override(const Json::Value& root, const bool load_cert_key) const override + { + static const char title[] = "json_override"; + + Config::Ptr ret(new Config); + + // inherit from self + ret->mode = mode; + ret->dh = dh; + ret->frame = frame; + ret->ssl_debug_level = ssl_debug_level; + ret->flags = flags; + ret->local_cert_enabled = local_cert_enabled; + + // ca + { + const std::string& ca_txt = json::get_string_ref(root, "ca", title); + ret->load_ca(ca_txt, true); + } + + // CRL + { + const std::string crl_txt = json::get_string_optional(root, "crl_verify", std::string(), title); + if (!crl_txt.empty()) + ret->load_crl(crl_txt); + } + + // cert/key + if (load_cert_key && local_cert_enabled) + { + bool loaded_cert = false; + + // cert/extra_certs + { + const std::string cert_txt = json::get_string_optional(root, "cert", std::string(), title); + if (!cert_txt.empty()) + { + const std::string ec_txt = json::get_string_optional(root, "extra_certs", std::string(), title); + ret->load_cert(cert_txt, ec_txt); + loaded_cert = true; + } + else + { + ret->cert = cert; + ret->extra_certs = extra_certs; + } + } + + // private key + if (loaded_cert && !external_pki) + { + const std::string& key_txt = json::get_string_ref(root, "key", title); + if (!key_txt.empty()) + ret->load_private_key(key_txt); + else + ret->pkey = pkey; + } + } + else + { + // inherit from self + ret->cert = cert; + ret->extra_certs = extra_certs; + ret->pkey = pkey; + } + + // ns_cert_type + { + const std::string ct = json::get_string_optional(root, "ns_cert_type", std::string(), title); + if (!ct.empty()) + ret->ns_cert_type = NSCert::ns_cert_type(ct); + } + + // ku, eku + { + const std::string ct = json::get_string_optional(root, "remote_cert_tls", std::string(), title); + if (!ct.empty()) + KUParse::remote_cert_tls(ct, ret->ku, ret->eku); + } + + // tls_version_min + { + const std::string tvm = json::get_string_optional(root, "tls_version_min", std::string(), title); + if (!tvm.empty()) + ret->tls_version_min = TLSVersion::parse_tls_version_min(tvm, false, maxver()); + } + + // tls_cert_profile + { + const std::string prof = json::get_string_optional(root, "tls_cert_profile", std::string(), title); + if (!prof.empty()) + ret->tls_cert_profile = TLSCertProfile::parse_tls_cert_profile(prof); + } + + return ret; + } +#endif + private: + static TLSVersion::Type maxver() + { + // Return maximum TLS version supported by OpenSSL. + // Assume that presence of SSL_OP_NO_TLSvX macro indicates + // that local OpenSSL library implements TLSvX. +#if defined(SSL_OP_NO_TLSv1_3) + return TLSVersion::V1_3; +#elif defined(SSL_OP_NO_TLSv1_2) + return TLSVersion::V1_2; +#elif defined(SSL_OP_NO_TLSv1_1) + return TLSVersion::V1_1; +#else + return TLSVersion::V1_0; +#endif + } + Mode mode; CertCRLList ca; // from OpenVPN "ca" and "crl-verify" option OpenSSLPKI::X509 cert; // from OpenVPN "cert" option @@ -409,9 +540,11 @@ namespace openvpn { OpenSSLPKI::DH dh; // diffie-hellman parameters (only needed in server mode) ExternalPKIBase* external_pki = nullptr; TLSSessionTicketBase* session_ticket_handler = nullptr; // server side only + SNI::HandlerBase* sni_handler = nullptr; // server side only Frame::Ptr frame; int ssl_debug_level = 0; unsigned int flags = 0; // defined in sslconsts.hpp + std::string sni_name; // client side only NSCert::Type ns_cert_type{NSCert::NONE}; std::vector ku; // if defined, peer cert X509 key usage must match one of these values std::string eku; // if defined, peer cert X509 extended key usage must match this OID/string @@ -433,12 +566,12 @@ namespace openvpn { public: typedef RCPtr Ptr; - virtual void start_handshake() + void start_handshake() override { SSL_do_handshake(ssl); } - virtual ssize_t write_cleartext_unbuffered(const void *data, const size_t size) + ssize_t write_cleartext_unbuffered(const void *data, const size_t size) override { const int status = BIO_write(ssl_bio, data, size); if (status < 0) @@ -455,7 +588,7 @@ namespace openvpn { return status; } - virtual ssize_t read_cleartext(void *data, const size_t capacity) + ssize_t read_cleartext(void *data, const size_t capacity) override { if (!overflow) { @@ -477,12 +610,12 @@ namespace openvpn { throw ssl_ciphertext_in_overflow(); } - virtual bool read_cleartext_ready() const + bool read_cleartext_ready() const override { return !bmq_stream::memq_from_bio(ct_in)->empty() || SSL_pending(ssl) > 0; } - virtual void write_ciphertext(const BufferPtr& buf) + void write_ciphertext(const BufferPtr& buf) override { bmq_stream::MemQ* in = bmq_stream::memq_from_bio(ct_in); if (in->size() < MAX_CIPHERTEXT_IN) @@ -491,7 +624,7 @@ namespace openvpn { overflow = true; } - virtual void write_ciphertext_unbuffered(const unsigned char *data, const size_t size) + void write_ciphertext_unbuffered(const unsigned char *data, const size_t size) override { bmq_stream::MemQ* in = bmq_stream::memq_from_bio(ct_in); if (in->size() < MAX_CIPHERTEXT_IN) @@ -500,17 +633,17 @@ namespace openvpn { overflow = true; } - virtual bool read_ciphertext_ready() const + bool read_ciphertext_ready() const override { return !bmq_stream::memq_from_bio(ct_out)->empty(); } - virtual BufferPtr read_ciphertext() + BufferPtr read_ciphertext() override { return bmq_stream::memq_from_bio(ct_out)->read_buf(); } - virtual std::string ssl_handshake_details() const + std::string ssl_handshake_details() const override { return ssl_handshake_details(ssl); } @@ -526,7 +659,7 @@ namespace openvpn { return !SSL_session_reused(ssl); } - virtual const AuthCert::Ptr& auth_cert() + const AuthCert::Ptr& auth_cert() const override { // Reused sessions don't call the cert verify callbacks, // so we must use an alternative method to build authcert. @@ -535,7 +668,7 @@ namespace openvpn { return authcert; } - virtual void mark_no_cache() + void mark_no_cache() override { sess_cache_key.reset(); } @@ -549,7 +682,7 @@ namespace openvpn { { bmq_stream::init_static(); - mydata_index = SSL_get_ex_new_index(0, (char *)"OpenSSLContext::SSL", nullptr, nullptr, nullptr); + ssl_data_index = SSL_get_ex_new_index(0, (char *)"OpenSSLContext::SSL", nullptr, nullptr, nullptr); context_data_index = SSL_get_ex_new_index(0, (char *)"OpenSSLContext", nullptr, nullptr, nullptr); /* @@ -592,7 +725,7 @@ namespace openvpn { SSL_set_mode(ssl, SSL_MODE_RELEASE_BUFFERS); // verify hostname - if (hostname) + if (hostname && !(ctx.config->flags & SSLConst::NO_VERIFY_HOSTNAME)) { X509_VERIFY_PARAM *param = SSL_get0_param(ssl); X509_VERIFY_PARAM_set_hostflags(param, 0); @@ -628,9 +761,18 @@ namespace openvpn { sess_cache_key.reset(new OpenSSLSessionCache::Key(*cache_key, ctx.sess_cache)); } SSL_set_connect_state(ssl); - if (ctx.config->flags & SSLConst::ENABLE_SNI) - if (SSL_set_tlsext_host_name(ssl, hostname) != 1) - throw OpenSSLException("OpenSSLContext::SSL: SSL_set_tlsext_host_name failed"); + + // client-side SNI + if (!ctx.config->sni_name.empty()) + { + if (SSL_set_tlsext_host_name(ssl, ctx.config->sni_name.c_str()) != 1) + throw OpenSSLException("OpenSSLContext::SSL: SSL_set_tlsext_host_name failed (sni_name)"); + } + else if ((ctx.config->flags & SSLConst::ENABLE_CLIENT_SNI) && hostname) + { + if (SSL_set_tlsext_host_name(ssl, hostname->c_str()) != 1) + throw OpenSSLException("OpenSSLContext::SSL: SSL_set_tlsext_host_name failed (hostname)"); + } } else OPENVPN_THROW(ssl_context_error, "OpenSSLContext::SSL: unknown client/server mode"); @@ -640,12 +782,10 @@ namespace openvpn { SSL_set_bio (ssl, ct_in, ct_out); BIO_set_ssl (ssl_bio, ssl, BIO_NOCLOSE); - if (mydata_index < 0) - throw ssl_context_error("OpenSSLContext::SSL: mydata_index is uninitialized"); - if (context_data_index < 0) - throw ssl_context_error("OpenSSLContext::SSL: context_data_index is uninitialized"); - SSL_set_ex_data (ssl, mydata_index, this); - SSL_set_ex_data (ssl, context_data_index, (void*) &ctx); + if (ssl_data_index < 0) + throw ssl_context_error("OpenSSLContext::SSL: ssl_data_index is uninitialized"); + SSL_set_ex_data (ssl, ssl_data_index, this); + set_parent(&ctx); } catch (...) { @@ -654,9 +794,15 @@ namespace openvpn { } } - void rebuild_authcert() + void set_parent(const OpenSSLContext* ctx) + { + if (context_data_index < 0) + throw ssl_context_error("OpenSSLContext::SSL: context_data_index is uninitialized"); + SSL_set_ex_data(ssl, context_data_index, (void *)ctx); + } + + void rebuild_authcert() const { - authcert.reset(new AuthCert()); ::X509 *cert = SSL_get_peer_certificate(ssl); if (cert) { @@ -684,17 +830,21 @@ namespace openvpn { } // Print a one line summary of SSL/TLS session handshake. - static std::string ssl_handshake_details (::SSL *c_ssl) + static std::string ssl_handshake_details (const ::SSL *c_ssl) { std::ostringstream os; + ::X509 *cert = SSL_get_peer_certificate (c_ssl); + + if (cert) + os << "CN=" << x509_get_field(cert, NID_commonName) << ", "; + os << SSL_get_version (c_ssl); const SSL_CIPHER *ciph = SSL_get_current_cipher (c_ssl); if (ciph) os << ", cipher " << SSL_CIPHER_get_version (ciph) << ' ' << SSL_CIPHER_get_name (ciph); - ::X509 *cert = SSL_get_peer_certificate (c_ssl); if (cert != nullptr) { EVP_PKEY *pkey = X509_get_pubkey (cert); @@ -710,7 +860,10 @@ namespace openvpn { } X509_free (cert); } - if (SSL_session_reused(c_ssl)) + // This has been changed in upstream SSL to have a const + // parameter, so we cast away const for older versions compatibility + // (Upstream commit: c04b66b18d1a90f0c6326858e4b8367be5444582) + if (SSL_session_reused(const_cast<::SSL *>(c_ssl))) os << " [REUSED]"; return os.str(); } @@ -792,12 +945,13 @@ namespace openvpn { BIO *ct_out; // read ciphertext from here AuthCert::Ptr authcert; OpenSSLSessionCache::Key::UPtr sess_cache_key; // client-side only + OpenSSLContext::Ptr sni_ctx; bool ssl_bio_linkage; bool overflow; bool called_did_full_handshake; // Helps us to store pointer to self in ::SSL object - static int mydata_index; + static int ssl_data_index; static int context_data_index; #if OPENSSL_VERSION_NUMBER < 0x10100000L @@ -1003,6 +1157,17 @@ namespace openvpn { throw OpenSSLException("OpenSSLContext: SSL_CTX_set_tmp_dh failed"); if (config->flags & SSLConst::SERVER_TO_SERVER) SSL_CTX_set_purpose(ctx, X509_PURPOSE_SSL_SERVER); + + // server-side SNI + if (config->sni_handler) + { +#if OPENSSL_VERSION_NUMBER >= 0x10101000L +#define OPENSSL_SERVER_SNI + SSL_CTX_set_client_hello_cb(ctx, client_hello_callback, nullptr); +#else + OPENVPN_THROW(ssl_context_error, "OpenSSLContext: server-side SNI requires OpenSSL 1.1 or higher"); +#endif + } } else if (config->mode.is_client()) { @@ -1024,11 +1189,8 @@ namespace openvpn { SSL_CTX_set_verify_depth(ctx, 16); } - long sslopt = SSL_OP_SINGLE_DH_USE | SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_COMPRESSION; - /* Disable SSLv2 and SSLv3, might be a noop but does not hurt */ - sslopt |= SSL_OP_NO_SSLv2; - sslopt |= SSL_OP_NO_SSLv3; + long sslopt = SSL_OP_SINGLE_DH_USE | SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_COMPRESSION | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3; if (config->mode.is_server()) { @@ -1044,6 +1206,16 @@ namespace openvpn { } else sslopt |= SSL_OP_NO_TICKET; + + // send a client CA list to the client + if (config->flags & SSLConst::SEND_CLIENT_CA_LIST) + { + for (const auto& e : config->ca.certs) + { + if (SSL_CTX_add_client_CA(ctx, e.obj()) != 1) + throw OpenSSLException("OpenSSLContext: SSL_CTX_add_client_CA failed"); + } + } } else { @@ -1062,23 +1234,20 @@ namespace openvpn { /* mbed TLS also ignores tls version when force aes cbc cipher suites is on */ if (!config->force_aes_cbc_ciphersuites) { - if (!config->force_aes_cbc_ciphersuites || config->tls_version_min > TLSVersion::UNDEF) - { - 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 -# ifdef SSL_OP_NO_TLSv1_3 - if (config->tls_version_min > TLSVersion::V1_3) - sslopt |= SSL_OP_NO_TLSv1_3; -# endif - } + 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 +# ifdef SSL_OP_NO_TLSv1_3 + if (config->tls_version_min > TLSVersion::V1_3) + sslopt |= SSL_OP_NO_TLSv1_3; +# endif } SSL_CTX_set_options(ctx, sslopt); @@ -1178,9 +1347,9 @@ namespace openvpn { // chain but shouldn't be included in the verify chain. if (config->extra_certs.defined()) { - for (OpenSSLPKI::X509List::const_iterator i = config->extra_certs.begin(); i != config->extra_certs.end(); ++i) + for (const auto& e : config->extra_certs) { - if (SSL_CTX_add_extra_chain_cert(ctx, (*i)->obj_dup()) != 1) + if (SSL_CTX_add_extra_chain_cert(ctx, e.obj_dup()) != 1) throw OpenSSLException("OpenSSLContext: SSL_CTX_add_extra_chain_cert failed"); } } @@ -1219,7 +1388,7 @@ namespace openvpn { void update_trust(const CertCRLList& cc) { OpenSSLPKI::X509Store store(cc); - SSL_CTX_set_cert_store(ctx, store.move()); + SSL_CTX_set_cert_store(ctx, store.release()); } ~OpenSSLContext() @@ -1528,7 +1697,7 @@ namespace openvpn { case X509_V_ERR_CERT_HAS_EXPIRED: return AuthCert::Fail::EXPIRED; default: - return AuthCert::Fail::OTHER; + return AuthCert::Fail::CERT_FAIL; } } @@ -1601,7 +1770,7 @@ namespace openvpn { const OpenSSLContext* self = (OpenSSLContext*) SSL_get_ex_data (ssl, SSL::context_data_index); // get OpenSSLContext::SSL - SSL* self_ssl = (SSL *) SSL_get_ex_data (ssl, SSL::mydata_index); + SSL* self_ssl = (SSL *) SSL_get_ex_data (ssl, SSL::ssl_data_index); // get error code const int err = X509_STORE_CTX_get_error(ctx); @@ -1678,9 +1847,7 @@ namespace openvpn { self->config->x509_track_config, *self_ssl->authcert->x509_track); - return preverify_ok || ((self->config->flags & SSLConst::DEFERRED_CERT_VERIFY) - && self_ssl->authcert // failsafe: don't defer error unless - && self_ssl->authcert->is_fail()); // authcert has recorded it + return preverify_ok || self->deferred_cert_verify_failsafe(*self_ssl); } // Print debugging information on SSL/TLS session negotiation. @@ -1806,6 +1973,139 @@ namespace openvpn { return true; } +#if OPENSSL_VERSION_NUMBER >= 0x10101000L + + static int client_hello_callback(::SSL *s, int *al, void *) + { + std::string sni_name; + + // get OpenSSLContext + OpenSSLContext* self = (OpenSSLContext*) SSL_get_ex_data(s, SSL::context_data_index); + + // get OpenSSLContext::SSL + SSL* self_ssl = (SSL *) SSL_get_ex_data(s, SSL::ssl_data_index); + + try { + // get the SNI from the client hello + sni_name = client_hello_get_sni(s); + + // process the SNI name, if provided + if (!sni_name.empty()) + { + // save the SNI name in authcert + if (self_ssl->authcert) + self_ssl->authcert->sni = sni_name; + + // ignore the SNI if no handler was provided + if (self->config->sni_handler) + { + // get an alternative SSLFactoryAPI from the sni_handler + SSLFactoryAPI::Ptr fapi; + try { + SNI::Metadata::UPtr sm; + fapi = self->config->sni_handler->sni_hello(sni_name, sm, self->config); + if (self_ssl->authcert) + self_ssl->authcert->sni_metadata = std::move(sm); + } + catch (const std::exception& e) + { + OPENVPN_LOG("SNI HANDLER ERROR: " << e.what()); + return sni_error(e.what(), SSL_AD_INTERNAL_ERROR, self, self_ssl, al); + } + if (!fapi) + return sni_error("SNI name not found", SSL_AD_UNRECOGNIZED_NAME, self, self_ssl, al); + + // make sure that returned SSLFactoryAPI is an OpenSSLContext + self_ssl->sni_ctx = fapi.dynamic_pointer_cast(); + if (!self_ssl->sni_ctx) + throw Exception("sni_handler returned wrong kind of SSLFactoryAPI"); + + // don't modify SSL CTX if the returned SSLFactoryAPI is ourself + if (fapi.get() != self) + { + SSL_set_SSL_CTX(s, self_ssl->sni_ctx->ctx); + self_ssl->set_parent(self_ssl->sni_ctx.get()); + } + } + } + return SSL_CLIENT_HELLO_SUCCESS; + } + catch (const std::exception& e) + { + OPENVPN_LOG("SNI exception in OpenSSLContext, SNI=" << sni_name << " : " << e.what()); + *al = SSL_AD_INTERNAL_ERROR; + return SSL_CLIENT_HELLO_ERROR; + } + } + + static int sni_error(std::string err, + const int ssl_ad_error, + OpenSSLContext* self, + SSL* self_ssl, + int* al) + { + if (self_ssl->authcert) + self_ssl->authcert->add_fail(0, AuthCert::Fail::SNI_ERROR, std::move(err)); + if (self->deferred_cert_verify_failsafe(*self_ssl)) + return SSL_CLIENT_HELLO_SUCCESS; + *al = ssl_ad_error; + return SSL_CLIENT_HELLO_ERROR; + } + + static size_t sni_get_len(ConstBuffer& buf) + { + size_t ret = buf.pop_front() << 8; + ret += buf.pop_front(); + return ret; + } + + static std::string client_hello_get_sni(::SSL* s) + { + const unsigned char *p; + size_t remaining; + if (!SSL_client_hello_get0_ext(s, TLSEXT_TYPE_server_name, &p, &remaining)) + return std::string(); + + // For safety, map a ConstBuffer onto returned OpenSSL TLSEXT_TYPE_server_name data. + ConstBuffer buf(p, remaining, true); + + // Extract the length of the supplied list of names, + // and check that it matches size of remaining data + // in buf. + { + const size_t len = sni_get_len(buf); + if (len != buf.size()) + throw Exception("bad name list size"); + } + + // Next byte must be TLSEXT_NAMETYPE_host_name. + if (buf.pop_front() != TLSEXT_NAMETYPE_host_name) + throw Exception("expecting TLSEXT_NAMETYPE_host_name"); + + // Now try to extract the SNI name. + { + const size_t len = sni_get_len(buf); + if (len > buf.size()) + throw Exception("bad name size"); + if (!Unicode::is_valid_utf8_uchar_buf(buf.c_data(), len, 1024 | Unicode::UTF8_NO_CTRL)) + throw Exception("invalid UTF-8"); + return std::string((const char *)buf.c_data(), len); + } + } +#endif + + // Return true if we should continue with authentication + // even though there was an error, because the user has + // enabled SSLConst::DEFERRED_CERT_VERIFY and wants the + // error to be logged in authcert so that it can be handled + // by a higher layer. + bool deferred_cert_verify_failsafe(const SSL& ssl) const + { + return (config->flags & SSLConst::DEFERRED_CERT_VERIFY) + && ssl.authcert // failsafe: don't defer error unless + && ssl.authcert->is_fail(); // authcert has recorded it + } + void erase() { if (epki) @@ -1826,13 +2126,19 @@ namespace openvpn { OpenSSLSessionCache::Ptr sess_cache; // client-side only }; - int OpenSSLContext::SSL::mydata_index = -1; +#ifdef OPENVPN_NO_EXTERN + int OpenSSLContext::SSL::ssl_data_index = -1; int OpenSSLContext::SSL::context_data_index = -1; +#endif -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if OPENSSL_VERSION_NUMBER < 0x10100000L && defined(OPENVPN_NO_EXTERN) SSL_METHOD OpenSSLContext::SSL::ssl23_method_client_; SSL_METHOD OpenSSLContext::SSL::ssl23_method_server_; #endif -} + inline const std::string get_ssl_library_version() + { + return OPENSSL_VERSION_TEXT; + } +} #endif diff --git a/openvpn/openssl/util/reseed.hpp b/openvpn/openssl/util/reseed.hpp new file mode 100644 index 0000000..f56192f --- /dev/null +++ b/openvpn/openssl/util/reseed.hpp @@ -0,0 +1,40 @@ +// 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 + +// seed OpenSSL's random number generator with /dev/urandom + +#include + +#include + +namespace openvpn { + inline void openssl_reseed_rng() + { + unsigned char entropy[64]; + + RandomAPI::Ptr rng(new DevURand); + rng->rand_bytes(entropy, sizeof(entropy)); + + RAND_seed(entropy, sizeof(entropy)); + } +} diff --git a/openvpn/openssl/util/tokenencrypt.hpp b/openvpn/openssl/util/tokenencrypt.hpp index 41ab797..e25fd1d 100644 --- a/openvpn/openssl/util/tokenencrypt.hpp +++ b/openvpn/openssl/util/tokenencrypt.hpp @@ -66,15 +66,17 @@ namespace openvpn { TokenEncrypt(const Key& key, const int mode) { ctx = EVP_CIPHER_CTX_new (); - EVP_CIPHER_CTX_init (ctx); + EVP_CIPHER_CTX_reset (ctx); if (!EVP_CipherInit_ex(ctx, EVP_aes_128_ecb(), nullptr, key.data, nullptr, mode)) - throw OpenSSLException("TokenEncrypt: EVP_CipherInit_ex[1] failed"); + { + EVP_CIPHER_CTX_free(ctx); + throw OpenSSLException("TokenEncrypt: EVP_CipherInit_ex[1] failed"); + } EVP_CIPHER_CTX_set_padding(ctx, 0); } ~TokenEncrypt() { - EVP_CIPHER_CTX_cleanup(ctx); EVP_CIPHER_CTX_free(ctx); } diff --git a/openvpn/pki/cclist.hpp b/openvpn/pki/cclist.hpp index 469e19f..55c84c0 100644 --- a/openvpn/pki/cclist.hpp +++ b/openvpn/pki/cclist.hpp @@ -35,7 +35,7 @@ namespace openvpn { // Parse a concatenated list of certs and CRLs (PEM format). // Abstracts CertList and CRLList, so can be used with any crypto lib. - // CertList and CRLList must define Item and ItemPtr types. + // CertList and CRLList must define Item type. template class CertCRLListTemplate { @@ -109,8 +109,7 @@ namespace openvpn { if (state == S_IN_CERT && line == cert_end) { try { - typename CertList::ItemPtr x509(new typename CertList::Item(item, title)); - cert_list->push_back(x509); + cert_list->emplace_back(item, title); } catch (const std::exception& e) { @@ -122,8 +121,7 @@ namespace openvpn { if (state == S_IN_CRL && line == crl_end) { try { - typename CRLList::ItemPtr crl(new typename CRLList::Item(item)); - crl_list->push_back(crl); + crl_list->emplace_back(item); } catch (const std::exception& e) { diff --git a/openvpn/pki/pktype.hpp b/openvpn/pki/pktype.hpp new file mode 100644 index 0000000..b6bcebb --- /dev/null +++ b/openvpn/pki/pktype.hpp @@ -0,0 +1,39 @@ +// 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 + +// private key types + +namespace openvpn { + namespace PKType { + + enum Type { + PK_UNKNOWN = 0, + PK_NONE, + PK_DSA, + PK_RSA, + PK_EC, + PK_ECDSA, + }; + + } +} diff --git a/openvpn/random/mtrandapi.hpp b/openvpn/random/mtrandapi.hpp index 7a9735b..ac46193 100644 --- a/openvpn/random/mtrandapi.hpp +++ b/openvpn/random/mtrandapi.hpp @@ -50,6 +50,11 @@ namespace openvpn { { } + MTRand(const rand_type::result_type seed) + : rng(seed) + { + } + // Random algorithm name virtual std::string name() const { @@ -59,7 +64,11 @@ namespace openvpn { // Return true if algorithm is crypto-strength virtual bool is_crypto() const { +#ifdef OPENVPN_INSECURE_RANDOM // DO NOT enable in production! + return true; +#else return false; +#endif } // Fill buffer with random bytes diff --git a/openvpn/random/randapi.hpp b/openvpn/random/randapi.hpp index 667eba0..b73adce 100644 --- a/openvpn/random/randapi.hpp +++ b/openvpn/random/randapi.hpp @@ -25,10 +25,12 @@ #define OPENVPN_MBEDTLS_UTIL_RANDAPI_H #include +#include #include #include #include +#include namespace openvpn { @@ -94,6 +96,41 @@ namespace openvpn { return start + rand_get_positive() % (end - start + 1); } + // Return a uniformly distributed random number in the range [0, end). + // This version is strictly 32-bit only and optimizes by avoiding + // integer division. + std::uint32_t randrange32(const std::uint32_t end) + { + std::uint32_t r; + rand_fill(r); + return rand32_distribute(r, end); + } + + // Return a uniformly distributed random number in the range [start, end]. + // This version is strictly 32-bit only and optimizes by avoiding + // integer division. + std::uint32_t randrange32(const std::uint32_t start, const std::uint32_t end) + { + if (start >= end) + return start; + else + return start + randrange32(end - start + 1); + } + + // Return a random byte + std::uint8_t randbyte() + { + std::uint8_t byte; + rand_fill(byte); + return byte; + } + + // Return a random boolean + bool randbool() + { + return bool(randbyte() & 1); + } + // Throw an exception if algorithm is not crypto-strength. // Be sure to always call this method before using an rng // for crypto purposes. diff --git a/openvpn/random/randistrib.hpp b/openvpn/random/randistrib.hpp new file mode 100644 index 0000000..bbc7d81 --- /dev/null +++ b/openvpn/random/randistrib.hpp @@ -0,0 +1,37 @@ +// 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-2019 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 + +namespace openvpn { + + // Return a uniformly distributed random number in the range [0, end) + // using seed as a random seed. This version is strictly 32-bit only + // and optimizes by avoiding integer division. + inline std::uint32_t rand32_distribute(const std::uint32_t seed, + const std::uint32_t end) + { + return (std::uint64_t(seed) * end) >> 32; + } + +} diff --git a/openvpn/server/manage.hpp b/openvpn/server/manage.hpp index fa34b3b..a1401ef 100644 --- a/openvpn/server/manage.hpp +++ b/openvpn/server/manage.hpp @@ -71,7 +71,7 @@ namespace openvpn { virtual std::uint64_t instance_id() const = 0; // return a JSON string describing connected user - virtual std::string describe_user() = 0; + virtual std::string describe_user(const bool show_userprop) = 0; // disconnect virtual void disconnect_user(const HaltRestart::Type type, diff --git a/openvpn/server/vpnservnetblock.hpp b/openvpn/server/vpnservnetblock.hpp index b578123..4003443 100644 --- a/openvpn/server/vpnservnetblock.hpp +++ b/openvpn/server/vpnservnetblock.hpp @@ -69,7 +69,7 @@ namespace openvpn { std::string to_string() const { - return '[' + net.to_string() + ',' + server_gw.to_string() + ']'; + return '[' + net.to_string() + '/' + std::to_string(prefix_len) + ',' + server_gw.to_string() + ']'; } IP::Addr net; diff --git a/openvpn/ssl/kuparse.hpp b/openvpn/ssl/kuparse.hpp index 45a2a51..68e1cd9 100644 --- a/openvpn/ssl/kuparse.hpp +++ b/openvpn/ssl/kuparse.hpp @@ -65,6 +65,23 @@ namespace openvpn { } } + inline TLSWebType remote_cert_type(const std::string& ct) + { + if (ct == "server") + return TLS_WEB_SERVER; + else if (ct == "client") + return TLS_WEB_CLIENT; + else + throw option_error("remote-cert-tls must be 'client' or 'server'"); + } + + inline void remote_cert_tls(const std::string& ct, + std::vector& ku, + std::string& eku) + { + remote_cert_tls(remote_cert_type(ct), ku, eku); + } + inline void remote_cert_tls(const OptionList& opt, const std::string& relay_prefix, std::vector& ku, @@ -75,12 +92,7 @@ namespace openvpn { if (o) { const std::string ct = o->get_optional(1, 16); - if (ct == "server") - wt = TLS_WEB_SERVER; - else if (ct == "client") - wt = TLS_WEB_CLIENT; - else - throw option_error("remote-cert-tls must be 'client' or 'server'"); + wt = remote_cert_type(ct); } remote_cert_tls(wt, ku, eku); } diff --git a/openvpn/ssl/nscert.hpp b/openvpn/ssl/nscert.hpp index 1343cb7..5a36d4a 100644 --- a/openvpn/ssl/nscert.hpp +++ b/openvpn/ssl/nscert.hpp @@ -38,17 +38,23 @@ namespace openvpn { SERVER }; - inline Type ns_cert_type(const OptionList& opt, const std::string& relay_prefix) { + inline Type ns_cert_type(const std::string& ct) + { + if (ct == "server") + return SERVER; + else if (ct == "client") + return CLIENT; + else + throw option_error("ns-cert-type must be 'client' or 'server'"); + } + + inline Type ns_cert_type(const OptionList& opt, const std::string& relay_prefix) + { const Option* o = opt.get_ptr(relay_prefix + "ns-cert-type"); if (o) { const std::string ct = o->get_optional(1, 16); - if (ct == "server") - return SERVER; - else if (ct == "client") - return CLIENT; - else - throw option_error("ns-cert-type must be 'client' or 'server'"); + return ns_cert_type(ct); } return NONE; } diff --git a/openvpn/ssl/proto.hpp b/openvpn/ssl/proto.hpp index 3cdd8e3..c838054 100644 --- a/openvpn/ssl/proto.hpp +++ b/openvpn/ssl/proto.hpp @@ -334,9 +334,6 @@ namespace openvpn { // extra peer info key/value pairs generated by client app PeerInfo::Set::Ptr extra_peer_info; - // GUI version, passed to server as IV_GUI_VER - std::string gui_version; - // op header bool enable_op32 = false; int remote_peer_id = -1; // -1 to disable @@ -627,7 +624,12 @@ namespace openvpn { // server pushes compression but client has compression disabled // degrade to asymmetric compression (downlink only) comp_ctx = CompressContext(meth, true); - OPENVPN_LOG("Server has pushed compressor " << comp_ctx.str() << ", but client has disabled compression, switching to asymmetric"); + if (!comp_ctx.is_any_stub(meth)) + { + OPENVPN_LOG("Server has pushed compressor " + << comp_ctx.str() + << ", but client has disabled compression, switching to asymmetric"); + } } } } @@ -803,8 +805,6 @@ namespace openvpn { std::ostringstream out; const char *compstr = nullptr; - if (!gui_version.empty()) - out << "IV_GUI_VER=" << gui_version << '\n'; out << "IV_VER=" << OPENVPN_VERSION << '\n'; out << "IV_PLAT=" << platform_name() << '\n'; if (!force_aes_cbc_ciphersuites) @@ -3491,8 +3491,8 @@ namespace openvpn { bool is_client() const { return mode_.is_client(); } // tcp/udp mode - const bool is_tcp() { return config->protocol.is_tcp(); } - const bool is_udp() { return config->protocol.is_udp(); } + bool is_tcp() { return config->protocol.is_tcp(); } + bool is_udp() { return config->protocol.is_udp(); } // configuration const Config& conf() const { return *config; } diff --git a/openvpn/ssl/sni_handler.hpp b/openvpn/ssl/sni_handler.hpp new file mode 100644 index 0000000..a1feb30 --- /dev/null +++ b/openvpn/ssl/sni_handler.hpp @@ -0,0 +1,52 @@ +// 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-2019 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 + +namespace openvpn { + namespace SNI { + + // Abstract base class used to provide an SNI handler + class HandlerBase + { + public: + typedef std::unique_ptr UPtr; + + // Return a new SSLFactoryAPI for this SNI name. + // Implementation may also set sni_metadata. + // Return SSLFactoryAPI::Ptr() if sni_name is not recognized. + // The caller guarantees that sni_name is valid UTF-8 and + // doesn't contain any control characters. + virtual SSLFactoryAPI::Ptr sni_hello(const std::string& sni_name, + SNI::Metadata::UPtr& sni_metadata, + SSLConfigAPI::Ptr default_factory) const = 0; + + virtual ~HandlerBase() {} + }; + + } +} diff --git a/openvpn/ssl/sni_metadata.hpp b/openvpn/ssl/sni_metadata.hpp new file mode 100644 index 0000000..5446f0c --- /dev/null +++ b/openvpn/ssl/sni_metadata.hpp @@ -0,0 +1,44 @@ +// 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-2019 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 { + + class AuthCert; + + namespace SNI { + + class Metadata + { + public: + typedef std::unique_ptr UPtr; + + virtual std::string sni_client_name(const AuthCert& ac) const = 0; + + virtual ~Metadata() = default; + }; + + } +} diff --git a/openvpn/ssl/sslapi.hpp b/openvpn/ssl/sslapi.hpp index c207a91..9258340 100644 --- a/openvpn/ssl/sslapi.hpp +++ b/openvpn/ssl/sslapi.hpp @@ -32,10 +32,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -46,6 +48,10 @@ namespace openvpn { + namespace SNI { + class HandlerBase; + } + class SSLAPI : public RC { public: @@ -67,7 +73,7 @@ namespace openvpn { virtual BufferPtr read_ciphertext() = 0; virtual std::string ssl_handshake_details() const = 0; virtual bool did_full_handshake() = 0; - virtual const AuthCert::Ptr& auth_cert() = 0; + virtual const AuthCert::Ptr& auth_cert() const = 0; virtual void mark_no_cache() = 0; // prevent caching of client-side session (only meaningful when client_session_tickets is enabled) uint32_t get_tls_warnings() const { @@ -103,15 +109,6 @@ namespace openvpn { public: typedef RCPtr Ptr; - enum PKType { - PK_UNKNOWN = 0, - PK_NONE, - PK_DSA, - PK_RSA, - PK_EC, - PK_ECDSA, - }; - enum LoadFlags { LF_PARSE_MODE = (1<<0), LF_ALLOW_CLIENT_CERT_NOT_REQUIRED = (1<<1), @@ -121,21 +118,21 @@ namespace openvpn { std::string private_key_type_string() const { - PKType type = private_key_type(); + PKType::Type type = private_key_type(); switch (type) { - case PK_NONE: + case PKType::PK_NONE: return "None"; - case PK_DSA: + case PKType::PK_DSA: return "DSA"; - case PK_RSA: + case PKType::PK_RSA: return "RSA"; - case PK_EC: + case PKType::PK_EC: return "EC"; - case PK_ECDSA: + case PKType::PK_ECDSA: return "ECDSA"; - case PK_UNKNOWN: + case PKType::PK_UNKNOWN: default: return "Unknown"; } @@ -146,6 +143,8 @@ namespace openvpn { virtual void set_external_pki_callback(ExternalPKIBase* external_pki_arg) = 0; // private key alternative virtual void set_session_ticket_handler(TLSSessionTicketBase* session_ticket_handler) = 0; // server side virtual void set_client_session_tickets(const bool v) = 0; // client side + virtual void set_sni_handler(SNI::HandlerBase* sni_handler) = 0; // server side + virtual void set_sni_name(const std::string& sni_name_arg) = 0; // client side virtual void set_private_key_password(const std::string& pwd) = 0; virtual void load_ca(const std::string& ca_txt, bool strict) = 0; virtual void load_crl(const std::string& crl_txt) = 0; @@ -159,7 +158,7 @@ namespace openvpn { virtual std::vector extract_extra_certs() const = 0; virtual std::string extract_private_key() const = 0; virtual std::string extract_dh() const = 0; - virtual PKType private_key_type() const = 0; + virtual PKType::Type private_key_type() const = 0; virtual size_t private_key_length() const = 0; virtual void set_frame(const Frame::Ptr& frame_arg) = 0; virtual void set_debug_level(const int debug_level) = 0; @@ -177,6 +176,10 @@ namespace openvpn { virtual void set_rng(const RandomAPI::Ptr& rng_arg) = 0; virtual void load(const OptionList& opt, const unsigned int lflags) = 0; +#ifdef HAVE_JSON + virtual SSLConfigAPI::Ptr json_override(const Json::Value& root, const bool load_cert_key) const = 0; +#endif + virtual std::string validate_cert(const std::string& cert_txt) const = 0; virtual std::string validate_cert_list(const std::string& certs_txt) const = 0; virtual std::string validate_crl(const std::string& crl_txt) const = 0; @@ -185,6 +188,15 @@ namespace openvpn { virtual SSLFactoryAPI::Ptr new_factory() = 0; }; + + /** + * Reports a human readable string of the SSL library in use and its version. + * E.g. mbed TLS 1.2.4 + * + * @return a human readable SSL library version string + */ + inline const std::string get_ssl_library_version(); + } #endif diff --git a/openvpn/ssl/sslchoose.hpp b/openvpn/ssl/sslchoose.hpp index eb07766..093ab7f 100644 --- a/openvpn/ssl/sslchoose.hpp +++ b/openvpn/ssl/sslchoose.hpp @@ -41,7 +41,7 @@ #include #include #include -#ifdef HAVE_OPENVPN_COMMON +#if defined(HAVE_OPENVPN_COMMON) && !defined(MBEDTLS_DISABLE_NAME_CONSTRAINTS) #include #endif #ifdef OPENVPN_PLATFORM_UWP @@ -61,7 +61,7 @@ namespace openvpn { #if defined(USE_MBEDTLS) #define SSL_LIB_NAME "MbedTLS" typedef MbedTLSCryptoAPI CryptoAPI; - #ifdef HAVE_OPENVPN_COMMON + #if defined(HAVE_OPENVPN_COMMON) && !defined(MBEDTLS_DISABLE_NAME_CONSTRAINTS) typedef MbedTLSContextNameConstraints SSLAPI; #else typedef MbedTLSContext SSLAPI; diff --git a/openvpn/ssl/sslconsts.hpp b/openvpn/ssl/sslconsts.hpp index 88bab41..9bc8006 100644 --- a/openvpn/ssl/sslconsts.hpp +++ b/openvpn/ssl/sslconsts.hpp @@ -44,25 +44,33 @@ namespace openvpn { // Disable peer verification NO_VERIFY_PEER=(1<<1), - // Enable SNI (Server Name Indication) when hostname is provided - ENABLE_SNI=(1<<2), + // [client only] Enable client-side SNI (Server Name Indication) + // when hostname is provided + ENABLE_CLIENT_SNI=(1<<2), + + // [client only] Don't require that the hostname matches + // the common name in the certificate. + NO_VERIFY_HOSTNAME=(1<<3), // [server only] Don't automatically fail connections on // bad peer cert. Succeed the connection, but pass the // fail status data via AuthCert so the higher layers // can handle it. - DEFERRED_CERT_VERIFY=(1<<3), + DEFERRED_CERT_VERIFY=(1<<4), // [server only] When running as a server, require that // clients that connect to us have their certificate // purpose set to server. - SERVER_TO_SERVER=(1<<4), + SERVER_TO_SERVER=(1<<5), // Peer certificate is optional - PEER_CERT_OPTIONAL=(1<<5), + PEER_CERT_OPTIONAL=(1<<6), + + // [server only] Send a list of client CAs to the client + SEND_CLIENT_CA_LIST=(1<<7), // last flag marker - LAST=(1<<6), + LAST=(1<<8) }; // filter all but SSL flags diff --git a/openvpn/time/asiotimersafe.hpp b/openvpn/time/asiotimersafe.hpp index d277072..06794d8 100644 --- a/openvpn/time/asiotimersafe.hpp +++ b/openvpn/time/asiotimersafe.hpp @@ -21,58 +21,71 @@ #pragma once +#include #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. +// AsioTimerSafe is like AsioTimer but with strict cancellation +// semantics that guarantees that a handler will never be called +// with a non-error status after the timer is cancelled. namespace openvpn { class AsioTimerSafe { public: - typedef std::size_t epoch_t; - AsioTimerSafe(openvpn_io::io_context& io_context) - : timer_(io_context) + : timer_(io_context), + epoch_(new Epoch) { } std::size_t expires_at(const Time& t) { - ++epoch_; + inc_epoch(); return timer_.expires_at(t); } std::size_t expires_after(const Time::Duration& d) { - ++epoch_; + inc_epoch(); return timer_.expires_after(d); } std::size_t cancel() { - ++epoch_; + inc_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 + inc_epoch(); + timer_.async_wait([func=std::move(func), epoch=epoch(), eptr=epoch_](const openvpn_io::error_code& error) { - func(error, epoch); + func(epoch == eptr->epoch ? error : openvpn_io::error::operation_aborted); }); } private: + typedef std::size_t epoch_t; + + struct Epoch : public RC + { + typedef RCPtr Ptr; + epoch_t epoch = 0; + }; + + epoch_t epoch() const + { + return epoch_->epoch; + } + + void inc_epoch() + { + ++epoch_->epoch; + } + AsioTimer timer_; - epoch_t epoch_ = 0; + Epoch::Ptr epoch_; }; } diff --git a/openvpn/time/epoch.hpp b/openvpn/time/epoch.hpp index 7bc13ff..ea90a11 100644 --- a/openvpn/time/epoch.hpp +++ b/openvpn/time/epoch.hpp @@ -27,6 +27,8 @@ namespace openvpn { + typedef std::uint64_t nanotime_t; + inline std::uint64_t milliseconds_since_epoch() { struct timespec ts; @@ -36,7 +38,7 @@ namespace openvpn { + std::uint64_t(ts.tv_nsec) / std::uint64_t(1000000); } - inline std::uint64_t nanoseconds_since_epoch() + inline nanotime_t nanoseconds_since_epoch() { struct timespec ts; if (::clock_gettime(CLOCK_REALTIME, &ts)) diff --git a/openvpn/time/skew.hpp b/openvpn/time/skew.hpp new file mode 100644 index 0000000..133bea6 --- /dev/null +++ b/openvpn/time/skew.hpp @@ -0,0 +1,53 @@ +// 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-2019 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 + +namespace openvpn { + + struct TimeSkew + { + // Skew factors (+/- %). + // Pass these to skew() via factor parameter. + enum { + PCT_50 = 0, + PCT_25 = 1, + PCT_12_5 = 2, + PCT_6_25 = 3, + PCT_3_125 = 4, + PCT_1_5625 = 5, + }; + + // Skew a duration by some random flux. + static Time::Duration skew(const Time::Duration& dur, const unsigned int factor, RandomAPI& prng) + { + const std::uint32_t bms = std::min(dur.to_binary_ms() >> factor, oulong(0x40000000)); // avoid 32-bit overflow in next step + const int flux = int(prng.randrange32(bms)) - int(bms/2); + return dur + flux; + } + }; + +} diff --git a/openvpn/transport/client/tcpcli.hpp b/openvpn/transport/client/tcpcli.hpp index 9fddd4c..5a7dbb0 100644 --- a/openvpn/transport/client/tcpcli.hpp +++ b/openvpn/transport/client/tcpcli.hpp @@ -53,6 +53,7 @@ namespace openvpn { #ifdef OPENVPN_TLS_LINK bool use_tls = false; + std::string tls_ca; #endif #ifdef OPENVPN_GREMLIN @@ -311,15 +312,26 @@ namespace openvpn { #ifdef OPENVPN_TLS_LINK if (config->use_tls) { + int flags = SSLConst::LOG_VERIFY_STATUS|SSLConst::ENABLE_CLIENT_SNI; SSLLib::SSLAPI::Config::Ptr ssl_conf; ssl_conf.reset(new SSLLib::SSLAPI::Config()); ssl_conf->set_mode(Mode(Mode::CLIENT)); - ssl_conf->set_flags(SSLConst::LOG_VERIFY_STATUS|SSLConst::NO_VERIFY_PEER| - SSLConst::ENABLE_SNI); ssl_conf->set_local_cert_enabled(false); ssl_conf->set_frame(config->frame); ssl_conf->set_rng(new SSLLib::RandomAPI(false)); + if (!config->tls_ca.empty()) + { + ssl_conf->load_ca(config->tls_ca, true); + } + else + { + flags |= SSLConst::NO_VERIFY_PEER; + } + + ssl_conf->set_flags(flags); + ssl_factory = ssl_conf->new_factory(); + impl.reset(new LinkImplTLS(this, io_context, socket, @@ -327,7 +339,7 @@ namespace openvpn { config->free_list_max_size, config->frame, config->stats, - ssl_conf->new_factory())); + ssl_factory)); } else #endif @@ -370,6 +382,10 @@ namespace openvpn { LinkImpl::Base::protocol::endpoint server_endpoint; bool halt; bool stop_requeueing; + +#ifdef OPENVPN_TLS_LINK + SSLFactoryAPI::Ptr ssl_factory; +#endif }; inline TransportClient::Ptr ClientConfig::new_transport_client_obj(openvpn_io::io_context& io_context, diff --git a/openvpn/transport/tcplinkcommon.hpp b/openvpn/transport/tcplinkcommon.hpp index 3a79b5c..7129730 100644 --- a/openvpn/transport/tcplinkcommon.hpp +++ b/openvpn/transport/tcplinkcommon.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -182,9 +183,22 @@ namespace openvpn { } catch (const std::exception& e) { + Error::Type err = Error::TCP_SIZE_ERROR; + const char *msg = "TCP_SIZE_ERROR"; + // if exception is an ExceptionCode, translate the code + // to return status string + { + const ExceptionCode *ec = dynamic_cast(&e); + if (ec && ec->code_defined()) + { + err = ec->code(); + msg = ec->what(); + } + } + OPENVPN_LOG_TCPLINK_ERROR("TCP packet extract exception: " << e.what()); - self->stats->error(Error::TCP_SIZE_ERROR); - self->read_handler->tcp_error_handler("TCP_SIZE_ERROR"); + self->stats->error(err); + self->read_handler->tcp_error_handler(msg); self->stop(); } }); diff --git a/openvpn/tun/persist/tunpersist.hpp b/openvpn/tun/persist/tunpersist.hpp index dc8d074..eaf1225 100644 --- a/openvpn/tun/persist/tunpersist.hpp +++ b/openvpn/tun/persist/tunpersist.hpp @@ -31,7 +31,7 @@ namespace openvpn { // TunPersistTemplate adds persistence capabilities onto TunWrapTemplate, // in order to implement logic for the persist-tun directive. - template + template class TunPersistTemplate : public TunWrapTemplate { public: @@ -47,7 +47,7 @@ namespace openvpn { } // Current persisted state - const TunProp::State::Ptr& state() const + const STATE& state() const { return state_; } @@ -124,7 +124,7 @@ namespace openvpn { // Possibly save tunnel fd/handle, state, and options. bool persist_tun_state(const typename SCOPED_OBJ::base_type obj, - const TunProp::State::Ptr& state) + const STATE& state) { if (!enable_persistence_ || !use_persisted_tun_) { @@ -151,7 +151,7 @@ namespace openvpn { const bool enable_persistence_; TunBuilderBase * const tb_; - TunProp::State::Ptr state_; + STATE state_; std::string options_; TunBuilderCapture::Ptr copt_; diff --git a/openvpn/tun/win/client/clientconfig.hpp b/openvpn/tun/win/client/clientconfig.hpp new file mode 100644 index 0000000..613e568 --- /dev/null +++ b/openvpn/tun/win/client/clientconfig.hpp @@ -0,0 +1,99 @@ +// 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 +#include +#include +#include + +namespace openvpn { + namespace TunWin { + + // These types manage the underlying TAP driver HANDLE + typedef openvpn_io::windows::stream_handle TAPStream; + typedef ScopedAsioStream ScopedTAPStream; + struct TunPersistState { + TunProp::State::Ptr state; + RingBuffer::Ptr ring_buffer; + + void reset() + { + state.reset(); + ring_buffer.reset(); + } + }; + typedef TunPersistTemplate TunPersist; + + class ClientConfig : public TunClientFactory + { + friend class Client; // accesses wfp + + public: + typedef RCPtr Ptr; + + TunProp::Config tun_prop; + int n_parallel = 8; // number of parallel async reads on tun socket + bool wintun = false; + + Frame::Ptr frame; + SessionStats::Ptr stats; + + Stop* stop = nullptr; + + TunPersist::Ptr tun_persist; + + TunWin::SetupFactory::Ptr tun_setup_factory; + + TunWin::SetupBase::Ptr new_setup_obj(openvpn_io::io_context& io_context) + { + if (tun_setup_factory) + return tun_setup_factory->new_setup_obj(io_context, wintun); + else + return new TunWin::Setup(io_context, wintun); + } + + static Ptr new_obj() + { + return new ClientConfig; + } + + virtual TunClient::Ptr new_tun_client_obj(openvpn_io::io_context& io_context, + TunClientParent& parent, + TransportClient* transcli) override; + + virtual void finalize(const bool disconnected) override + { + if (disconnected) + tun_persist.reset(); + } + + virtual bool layer_2_supported() const override + { + return true; + } + }; + } +} + diff --git a/openvpn/tun/win/client/setupbase.hpp b/openvpn/tun/win/client/setupbase.hpp index 6c8a29b..7b10762 100644 --- a/openvpn/tun/win/client/setupbase.hpp +++ b/openvpn/tun/win/client/setupbase.hpp @@ -35,6 +35,8 @@ #include #include +#include + namespace openvpn { namespace TunWin { struct SetupBase : public DestructorBase @@ -46,7 +48,8 @@ namespace openvpn { virtual HANDLE establish(const TunBuilderCapture& pull, const std::wstring& openvpn_app_path, Stop* stop, - std::ostream& os) = 0; + std::ostream& os, + RingBuffer::Ptr rings) = 0; virtual bool l2_ready(const TunBuilderCapture& pull) = 0; diff --git a/openvpn/tun/win/client/tuncli.hpp b/openvpn/tun/win/client/tuncli.hpp index 8b7b2ab..f2b301a 100644 --- a/openvpn/tun/win/client/tuncli.hpp +++ b/openvpn/tun/win/client/tuncli.hpp @@ -38,12 +38,10 @@ #include #include #include +#include #include #include - -#define OPENVPN_WINTUN_START_PADDING_LEN 12 -#define OPENVPN_WINTUN_PACKET_SIZE_LEN 4 -#define OPENVPN_WINTUN_PACKET_ALIGN 16 +#include namespace openvpn { namespace TunWin { @@ -71,9 +69,8 @@ namespace openvpn { const bool retain_stream, ReadHandler read_handler, const Frame::Ptr& frame, - const SessionStats::Ptr& stats, - const bool wintun) - : Base(read_handler, frame, stats, wintun ? Frame::READ_WINTUN : Frame::READ_TUN) + const SessionStats::Ptr& stats) + : Base(read_handler, frame, stats, Frame::READ_TUN) { Base::name_ = name; Base::retain_stream = retain_stream; @@ -81,60 +78,6 @@ namespace openvpn { } }; - // These types manage the underlying TAP driver HANDLE - typedef openvpn_io::windows::stream_handle TAPStream; - typedef ScopedAsioStream ScopedTAPStream; - typedef TunPersistTemplate TunPersist; - - class ClientConfig : public TunClientFactory - { - friend class Client; // accesses wfp - - public: - typedef RCPtr Ptr; - - TunProp::Config tun_prop; - int n_parallel = 8; // number of parallel async reads on tun socket - bool wintun = false; // wintun may return up to 256 packets - - Frame::Ptr frame; - SessionStats::Ptr stats; - - Stop* stop = nullptr; - - TunPersist::Ptr tun_persist; - - TunWin::SetupFactory::Ptr tun_setup_factory; - - TunWin::SetupBase::Ptr new_setup_obj(openvpn_io::io_context& io_context) - { - if (tun_setup_factory) - return tun_setup_factory->new_setup_obj(io_context, wintun); - else - return new TunWin::Setup(io_context, wintun); - } - - static Ptr new_obj() - { - return new ClientConfig; - } - - virtual TunClient::Ptr new_tun_client_obj(openvpn_io::io_context& io_context, - TunClientParent& parent, - TransportClient* transcli) override; - - virtual void finalize(const bool disconnected) override - { - if (disconnected) - tun_persist.reset(); - } - - virtual bool layer_2_supported() const override - { - return true; - } - }; - class Client : public TunClient { friend class ClientConfig; // calls constructor @@ -161,7 +104,7 @@ namespace openvpn { // Check if persisted tun session matches properties of to-be-created session if (tun_persist->use_persisted_tun(server_addr, config->tun_prop, opt)) { - state = tun_persist->state(); + state = tun_persist->state().state; OPENVPN_LOG("TunPersist: reused tun context"); } else @@ -192,14 +135,14 @@ namespace openvpn { { std::ostringstream os; auto os_print = Cleanup([&os](){ OPENVPN_LOG_STRING(os.str()); }); - th = tun_setup->establish(*po, Win::module_name(), config->stop, os); + th = tun_setup->establish(*po, Win::module_name(), config->stop, os, NULL); } // create ASIO wrapper for HANDLE TAPStream* ts = new TAPStream(io_context, th); // persist tun settings state - if (tun_persist->persist_tun_state(ts, state)) + if (tun_persist->persist_tun_state(ts, { state, nullptr })) OPENVPN_LOG("TunPersist: saving tun context:" << std::endl << tun_persist->options()); // setup handler for external tun close @@ -225,8 +168,7 @@ namespace openvpn { true, this, config->frame, - config->stats, - config->wintun)); + config->stats)); impl->start(config->n_parallel); if (!dhcp_capture) @@ -319,19 +261,6 @@ namespace openvpn { if (dhcp_capture) dhcp_inspect(buf); - if (config->wintun) - { - // end padding - auto packet_size = buf.size(); - auto end_padding_size = OPENVPN_WINTUN_PACKET_ALIGN - - (buf.size() & (OPENVPN_WINTUN_PACKET_ALIGN - 1)); - buf.write(wintun_padding, end_padding_size); - - // start padding and size - buf.prepend(wintun_padding, OPENVPN_WINTUN_START_PADDING_LEN); - buf.prepend((unsigned char *)&packet_size, OPENVPN_WINTUN_PACKET_SIZE_LEN); - } - return impl->write(buf); } else @@ -343,34 +272,7 @@ namespace openvpn { void tun_read_handler(PacketFrom::SPtr& pfp) // called by TunImpl { - if (config->wintun) - { - // we might receive up to 256 packets - while (pfp->buf.size() > 0) - { - // parse wintun encapsulation - auto packet_size = *(std::uint32_t*)pfp->buf.c_data(); - pfp->buf.advance(OPENVPN_WINTUN_PACKET_SIZE_LEN); - pfp->buf.advance(OPENVPN_WINTUN_START_PADDING_LEN); - - // extract individual packet - frame_context.prepare(wintun_packet); - wintun_packet.write(pfp->buf.c_data(), packet_size); - - parent.tun_recv(wintun_packet); - - // skip to the next packet - pfp->buf.advance(packet_size); - - auto padding = (OPENVPN_WINTUN_PACKET_ALIGN - - (packet_size & (OPENVPN_WINTUN_PACKET_ALIGN - 1))) % OPENVPN_WINTUN_PACKET_ALIGN; - pfp->buf.advance(padding); - } - } - else - { - parent.tun_recv(pfp->buf); - } + parent.tun_recv(pfp->buf); #ifdef OPENVPN_DEBUG_TAPWIN tap_process_logging(); @@ -487,8 +389,6 @@ namespace openvpn { std::unique_ptr dhcp_capture; AsioTimer l2_timer; - unsigned char wintun_padding[OPENVPN_WINTUN_PACKET_ALIGN] = {}; - BufferAllocated wintun_packet; Frame::Context& frame_context; bool halt; @@ -498,7 +398,10 @@ namespace openvpn { TunClientParent& parent, TransportClient* transcli) { - return TunClient::Ptr(new Client(io_context, this, parent)); + if (wintun) + return TunClient::Ptr(new WintunClient(io_context, this, parent)); + else + return TunClient::Ptr(new Client(io_context, this, parent)); } } diff --git a/openvpn/tun/win/client/tunsetup.hpp b/openvpn/tun/win/client/tunsetup.hpp index c902572..1bec60a 100644 --- a/openvpn/tun/win/client/tunsetup.hpp +++ b/openvpn/tun/win/client/tunsetup.hpp @@ -68,7 +68,8 @@ namespace openvpn { virtual HANDLE establish(const TunBuilderCapture& pull, const std::wstring& openvpn_app_path, Stop* stop, - std::ostream& os) override // defined by SetupBase + std::ostream& os, + RingBuffer::Ptr ring_buffer) override // defined by SetupBase { // close out old remove cmds, if they exist destroy(os); @@ -104,7 +105,7 @@ namespace openvpn { switch (pull.layer()) { case Layer::OSI_LAYER_3: - adapter_config(th(), openvpn_app_path, tap, wintun, pull, false, *add_cmds, *remove_cmds, os); + adapter_config(th(), openvpn_app_path, tap, pull, false, *add_cmds, *remove_cmds, os); break; case Layer::OSI_LAYER_2: adapter_config_l2(th(), openvpn_app_path, tap, pull, *add_cmds, *remove_cmds, os); @@ -123,6 +124,9 @@ namespace openvpn { if (pull.layer() == Layer::OSI_LAYER_2) l2_state.reset(new L2State(tap, openvpn_app_path)); + if (ring_buffer) + register_rings(th(), ring_buffer); + return th.release(); } @@ -162,7 +166,7 @@ namespace openvpn { { Win::ScopedHANDLE nh; ActionList::Ptr add_cmds(new ActionList()); - adapter_config(nh(), l2s->openvpn_app_path, l2s->tap, wintun, pull, true, *add_cmds, *remove_cmds, os); + adapter_config(nh(), l2s->openvpn_app_path, l2s->tap, pull, true, *add_cmds, *remove_cmds, os); add_cmds->execute(os); } } @@ -250,12 +254,36 @@ namespace openvpn { int indices[2] = {0, 0}; }; + void register_rings(HANDLE handle, RingBuffer::Ptr ring_buffer) + { + TUN_REGISTER_RINGS rings; + + ZeroMemory(&rings, sizeof(rings)); + + rings.receive.ring = ring_buffer->receive_ring(); + rings.receive.tail_moved = ring_buffer->receive_ring_tail_moved(); + rings.receive.ring_size = sizeof(rings.receive.ring->data); + + rings.send.ring = ring_buffer->send_ring(); + rings.send.tail_moved = ring_buffer->send_ring_tail_moved(); + rings.send.ring_size = sizeof(rings.send.ring->data); + + { + Win::Impersonate imp(true); + + if (!DeviceIoControl(handle, TUN_IOCTL_REGISTER_RINGS, &rings, sizeof(rings), NULL, NULL, NULL, NULL)) + { + const Win::LastError err; + throw ErrorCode(Error::TUN_REGISTER_RINGS_ERROR, true, "Error registering ring buffers: " + err.message()); + } + } + } + #if _WIN32_WINNT >= 0x0600 // Configure TAP adapter on Vista and higher void adapter_config(HANDLE th, const std::wstring& openvpn_app_path, const Util::TapNameGuidPair& tap, - bool wintun, const TunBuilderCapture& pull, const bool l2_post, ActionList& create, diff --git a/openvpn/tun/win/client/wintun.hpp b/openvpn/tun/win/client/wintun.hpp new file mode 100644 index 0000000..cd7e52f --- /dev/null +++ b/openvpn/tun/win/client/wintun.hpp @@ -0,0 +1,318 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace openvpn { + namespace TunWin { + + class WintunClient : public TunClient + { + typedef RCPtr Ptr; + + public: + WintunClient(openvpn_io::io_context& io_context_arg, + ClientConfig* config_arg, + TunClientParent& parent_arg) + : io_context(io_context_arg), + config(config_arg), + parent(parent_arg), + state(new TunProp::State()), + frame(config_arg->frame) + { + } + + // Inherited via TunClient + void tun_start(const OptionList& opt, TransportClient& transcli, CryptoDCSettings&) override + { + halt = false; + if (config->tun_persist) + tun_persist = config->tun_persist; // long-term persistent + else + tun_persist.reset(new TunPersist(false, false, nullptr)); // short-term + + try { + + const IP::Addr server_addr = transcli.server_endpoint_addr(); + + // Check if persisted tun session matches properties of to-be-created session + if (tun_persist->use_persisted_tun(server_addr, config->tun_prop, opt)) + { + state = tun_persist->state().state; + ring_buffer = tun_persist->state().ring_buffer; + OPENVPN_LOG("TunPersist: reused tun context"); + } + else + { + // notify parent + parent.tun_pre_tun_config(); + + // close old TAP handle if persisted + tun_persist->close(); + + // parse pushed options + TunBuilderCapture::Ptr po(new TunBuilderCapture()); + TunProp::configure_builder(po.get(), + state.get(), + config->stats.get(), + server_addr, + config->tun_prop, + opt, + nullptr, + false); + OPENVPN_LOG("CAPTURED OPTIONS:" << std::endl << po->to_string()); + + // create new tun setup object + tun_setup = config->new_setup_obj(io_context); + + ring_buffer.reset(new RingBuffer(io_context)); + + // open/config TAP + HANDLE th; + { + std::ostringstream os; + auto os_print = Cleanup([&os]() { OPENVPN_LOG_STRING(os.str()); }); + th = tun_setup->establish(*po, Win::module_name(), config->stop, os, ring_buffer); + } + + // create ASIO wrapper for HANDLE + TAPStream* ts = new TAPStream(io_context, th); + + // persist tun settings state + if (tun_persist->persist_tun_state(ts, { state, ring_buffer })) + OPENVPN_LOG("TunPersist: saving tun context:" << std::endl << tun_persist->options()); + + // enable tun_setup destructor + tun_persist->add_destructor(tun_setup); + + // assert ownership over TAP device handle + tun_setup->confirm(); + } + + openvpn_io::post(io_context, [self=Ptr(this)](){ + self->read(); + }); + + parent.tun_connected(); // signal that we are connected + } + catch (const std::exception& e) + { + stop(); + Error::Type err = Error::TUN_SETUP_FAILED; + const ExceptionCode* ec = dynamic_cast(&e); + if (ec && ec->code_defined()) + err = ec->code(); + parent.tun_error(err, e.what()); + } + } + + void stop() override + { + if (!halt) + { + halt = true; + + tun_persist.reset(); + } + } + + void set_disconnect() override + { + + } + + bool tun_send(BufferAllocated& buf) override + { + TUN_RING* receive_ring = ring_buffer->receive_ring(); + + ULONG head = receive_ring->head.load(std::memory_order_acquire); + if (head > WINTUN_RING_CAPACITY) + { + if (head == 0xFFFFFFFF) + parent.tun_error(Error::TUN_WRITE_ERROR, "invalid ring head/tail or bogus packet received"); + return false; + } + + ULONG tail = receive_ring->tail.load(std::memory_order_acquire); + if (tail >= WINTUN_RING_CAPACITY) + return false; + + ULONG aligned_packet_size = packet_align(sizeof(TUN_PACKET_HEADER) + buf.size()); + ULONG buf_space = wrap(head - tail - WINTUN_PACKET_ALIGN); + if (aligned_packet_size > buf_space) + { + OPENVPN_LOG("ring is full"); + return false; + } + + // copy packet size and data into ring + TUN_PACKET* packet = (TUN_PACKET*)& receive_ring->data[tail]; + packet->size = buf.size(); + std::memcpy(packet->data, buf.data(), buf.size()); + + // move ring tail + receive_ring->tail.store(wrap(tail + aligned_packet_size), std::memory_order_release); + if (receive_ring->alertable.load(std::memory_order_acquire) != 0) + SetEvent(ring_buffer->receive_ring_tail_moved()); + + return true; + } + + std::string tun_name() const override + { + return "wintun"; + } + + std::string vpn_ip4() const override + { + if (state->vpn_ip4_addr.specified()) + return state->vpn_ip4_addr.to_string(); + else + return ""; + } + + std::string vpn_ip6() const override + { + if (state->vpn_ip6_addr.specified()) + return state->vpn_ip6_addr.to_string(); + else + return ""; + } + + std::string vpn_gw4() const override + { + if (state->vpn_ip4_gw.specified()) + return state->vpn_ip4_gw.to_string(); + else + return ""; + } + + std::string vpn_gw6() const override + { + if (state->vpn_ip6_gw.specified()) + return state->vpn_ip6_gw.to_string(); + else + return ""; + } + + private: + void read() + { + TUN_RING* send_ring = ring_buffer->send_ring(); + + if (halt) + return; + + ULONG head = send_ring->head.load(std::memory_order_acquire); + if (head >= WINTUN_RING_CAPACITY) + { + parent.tun_error(Error::TUN_ERROR, "ring head exceeds ring capacity"); + return; + } + + ULONG tail = send_ring->tail.load(std::memory_order_acquire); + if (tail >= WINTUN_RING_CAPACITY) + { + parent.tun_error(Error::TUN_ERROR, "ring tail exceeds ring capacity"); + return; + } + + while (true) + { + // tail has moved? + if (head == tail) + { + ring_buffer->send_tail_moved_asio_event().async_wait([self = Ptr(this)](const openvpn_io::error_code& error) { + if (!error) + self->read(); + else + { + if (!self->halt) + self->parent.tun_error(Error::TUN_ERROR, "error waiting on ring send tail moved"); + } + }); + return; + } + + // read buffer content + ULONG content_len = wrap(tail - head); + if (content_len < sizeof(TUN_PACKET_HEADER)) + { + parent.tun_error(Error::TUN_ERROR, "incomplete packet header in send ring"); + return; + } + + TUN_PACKET* packet = (TUN_PACKET *)&send_ring->data[head]; + if (packet->size > WINTUN_MAX_PACKET_SIZE) + { + parent.tun_error(Error::TUN_ERROR, "packet too big in send ring"); + return; + } + + ULONG aligned_packet_size = packet_align(sizeof(TUN_PACKET_HEADER) + packet->size); + if (aligned_packet_size > content_len) + { + parent.tun_error(Error::TUN_ERROR, "incomplete packet in send ring"); + return; + } + + frame->prepare(Frame::READ_TUN, buf); + + buf.write(packet->data, packet->size); + + head = wrap(head + aligned_packet_size); + send_ring->head.store(head, std::memory_order_release); + + parent.tun_recv(buf); + + if (halt) + return; + } + } + + struct TUN_PACKET_HEADER + { + uint32_t size; + }; + + struct TUN_PACKET + { + uint32_t size; + UCHAR data[WINTUN_MAX_PACKET_SIZE]; + }; + + ULONG packet_align(ULONG size) + { + return (size + (WINTUN_PACKET_ALIGN - 1)) & ~(WINTUN_PACKET_ALIGN - 1); + } + + ULONG wrap(ULONG value) + { + return value & (WINTUN_RING_CAPACITY - 1); + } + + openvpn_io::io_context& io_context; + TunPersist::Ptr tun_persist; // contains the TAP device HANDLE + ClientConfig::Ptr config; + TunClientParent& parent; + TunProp::State::Ptr state; + TunWin::SetupBase::Ptr tun_setup; + + TUN_RING* receive_ring = nullptr; + TUN_RING* send_ring = nullptr; + + BufferAllocated buf; + + Frame::Ptr frame; + + bool halt = false; + + ScopedHANDLE driver_handle; + + RingBuffer::Ptr ring_buffer; + }; + } +} diff --git a/openvpn/tun/win/ringbuffer.hpp b/openvpn/tun/win/ringbuffer.hpp new file mode 100644 index 0000000..bae3a79 --- /dev/null +++ b/openvpn/tun/win/ringbuffer.hpp @@ -0,0 +1,172 @@ +// 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 +#include + +#include +#include +#include +#include + +#define TUN_IOCTL_REGISTER_RINGS CTL_CODE(51820U, 0x970U, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA) +#define TUN_IOCTL_FORCE_CLOSE_HANDLES CTL_CODE(51820U, 0x971U, METHOD_NEITHER, FILE_READ_DATA | FILE_WRITE_DATA) + +#define WINTUN_RING_CAPACITY 0x800000 +#define WINTUN_RING_TRAILING_BYTES 0x10000 +#define WINTUN_RING_FRAMING_SIZE 12 +#define WINTUN_MAX_PACKET_SIZE 0xffff +#define WINTUN_PACKET_ALIGN 4 + +namespace openvpn +{ + namespace TunWin + { + struct TUN_RING { + std::atomic_ulong head; + std::atomic_ulong tail; + std::atomic_long alertable; + UCHAR data[WINTUN_RING_CAPACITY + WINTUN_RING_TRAILING_BYTES + WINTUN_RING_FRAMING_SIZE]; + }; + + struct TUN_REGISTER_RINGS + { + struct + { + ULONG ring_size; + TUN_RING* ring; + HANDLE tail_moved; + } send, receive; + }; + + typedef openvpn_io::windows::object_handle AsioEvent; + + class RingBuffer : public RC + { + public: + typedef RCPtr Ptr; + + RingBuffer(openvpn_io::io_context& io_context) + : send_ring_hmem(CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(TUN_RING), NULL)), + receive_ring_hmem(CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(TUN_RING), NULL)), + send_tail_moved_asio_event_(io_context) + { + // sanity checks + static_assert((sizeof(TUN_RING) - sizeof(TUN_RING::data)) == 12, "sizeof(TUN_RING) is expected to be 12"); +#if !defined(ATOMIC_LONG_LOCK_FREE) || (ATOMIC_LONG_LOCK_FREE != 2) +#error Atomic long is expected to be always lock-free +#endif + + send_ring_ = (TUN_RING*)MapViewOfFile(send_ring_hmem(), FILE_MAP_ALL_ACCESS, 0, 0, sizeof(TUN_RING)); + receive_ring_ = (TUN_RING*)MapViewOfFile(receive_ring_hmem(), FILE_MAP_ALL_ACCESS, 0, 0, sizeof(TUN_RING)); + send_tail_moved_asio_event_.assign(send_ring_tail_moved_()); + } + + RingBuffer(openvpn_io::io_context& io_context, + HANDLE client_process, + const std::string& send_ring_hmem_hex, + const std::string& receive_ring_hmem_hex, + const std::string& send_ring_tail_moved_hex, + const std::string& receive_ring_tail_moved_hex) + : send_tail_moved_asio_event_(io_context) + { + HANDLE remote_handle = BufHex::parse(send_ring_hmem_hex, "send_ring_hmem"); + HANDLE handle; + DuplicateHandle(client_process, remote_handle, GetCurrentProcess(), &handle, 0, FALSE, DUPLICATE_SAME_ACCESS); + send_ring_hmem.reset(handle); + + remote_handle = BufHex::parse(receive_ring_hmem_hex, "receive_ring_hmem"); + DuplicateHandle(client_process, remote_handle, GetCurrentProcess(), &handle, 0, FALSE, DUPLICATE_SAME_ACCESS); + receive_ring_hmem.reset(handle); + + remote_handle = BufHex::parse(send_ring_tail_moved_hex, "send_ring_tail_moved"); + DuplicateHandle(client_process, remote_handle, GetCurrentProcess(), &handle, 0, FALSE, DUPLICATE_SAME_ACCESS); + send_ring_tail_moved_.reset(handle); + + remote_handle = BufHex::parse(receive_ring_tail_moved_hex, "receive_ring_tail_moved"); + DuplicateHandle(client_process, remote_handle, GetCurrentProcess(), &handle, 0, FALSE, DUPLICATE_SAME_ACCESS); + receive_ring_tail_moved_.reset(handle); + + send_ring_ = (TUN_RING*)MapViewOfFile(send_ring_hmem(), FILE_MAP_ALL_ACCESS, 0, 0, sizeof(TUN_RING)); + receive_ring_ = (TUN_RING*)MapViewOfFile(receive_ring_hmem(), FILE_MAP_ALL_ACCESS, 0, 0, sizeof(TUN_RING)); + } + + RingBuffer(RingBuffer const&) = delete; + RingBuffer& operator=(RingBuffer const&) = delete; + + ~RingBuffer() + { + UnmapViewOfFile(send_ring_); + UnmapViewOfFile(receive_ring_); + } + + HANDLE send_ring_tail_moved() + { + return send_ring_tail_moved_(); + } + + HANDLE receive_ring_tail_moved() + { + return receive_ring_tail_moved_(); + } + + TUN_RING* send_ring() + { + return send_ring_; + } + + TUN_RING* receive_ring() + { + return receive_ring_; + } + + AsioEvent& send_tail_moved_asio_event() + { + return send_tail_moved_asio_event_; + } + +#ifdef HAVE_JSON + void serialize(Json::Value& json) + { + json["send_ring_hmem"] = BufHex::render(send_ring_hmem()); + json["receive_ring_hmem"] = BufHex::render(receive_ring_hmem()); + json["send_ring_tail_moved"] = BufHex::render(send_ring_tail_moved()); + json["receive_ring_tail_moved"] = BufHex::render(receive_ring_tail_moved()); + } +#endif + + protected: + Win::ScopedHANDLE send_ring_hmem; + Win::ScopedHANDLE receive_ring_hmem; + Win::Event send_ring_tail_moved_{FALSE}; + Win::Event receive_ring_tail_moved_{FALSE}; + AsioEvent send_tail_moved_asio_event_; + + TUN_RING* send_ring_ = nullptr; + TUN_RING* receive_ring_ = nullptr; + }; + } +} diff --git a/openvpn/tun/win/tunutil.hpp b/openvpn/tun/win/tunutil.hpp index 6b93056..12f4563 100644 --- a/openvpn/tun/win/tunutil.hpp +++ b/openvpn/tun/win/tunutil.hpp @@ -35,6 +35,12 @@ #include // for IPv6 #include // for impersonating as LocalSystem + +#include +#include +#include +#include + #include #include #include @@ -342,93 +348,123 @@ namespace openvpn { } }; - inline HANDLE impersonate_as_system() + struct DeviceInstanceIdInterfacePair { - HANDLE thread_token, process_snapshot, winlogon_process, winlogon_token, duplicated_token, file_handle; - PROCESSENTRY32 entry = {}; - entry.dwSize = sizeof(PROCESSENTRY32); - BOOL ret; - DWORD pid = 0; - TOKEN_PRIVILEGES privileges = {}; - privileges.PrivilegeCount = 1; - privileges.Privileges->Attributes = SE_PRIVILEGE_ENABLED; + std::string net_cfg_instance_id; + std::string device_interface_list; + }; - if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &privileges.Privileges[0].Luid)) - return INVALID_HANDLE_VALUE; - if (!ImpersonateSelf(SecurityImpersonation)) - return INVALID_HANDLE_VALUE; - if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, FALSE, &thread_token)) - { - RevertToSelf(); - return INVALID_HANDLE_VALUE; - } - if (!AdjustTokenPrivileges(thread_token, FALSE, &privileges, sizeof(privileges), NULL, NULL)) - { - CloseHandle(thread_token); - RevertToSelf(); - return INVALID_HANDLE_VALUE; - } - CloseHandle(thread_token); + class DevInfoSetHelper + { + public: + DevInfoSetHelper() + { + handle = SetupDiGetClassDevsEx(&GUID_DEVCLASS_NET, NULL, NULL, DIGCF_PRESENT, NULL, NULL, NULL); + } - process_snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - if (process_snapshot == INVALID_HANDLE_VALUE) - { - RevertToSelf(); - return INVALID_HANDLE_VALUE; - } - for (ret = Process32First(process_snapshot, &entry); ret; ret = Process32Next(process_snapshot, &entry)) - { - if (!_stricmp(entry.szExeFile, "winlogon.exe")) - { - pid = entry.th32ProcessID; - break; - } - } - CloseHandle(process_snapshot); - if (!pid) - { - RevertToSelf(); - return INVALID_HANDLE_VALUE; - } + bool is_valid() + { + return handle != INVALID_HANDLE_VALUE; + } - winlogon_process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid); - if (!winlogon_process) - { - RevertToSelf(); - return INVALID_HANDLE_VALUE; - } + operator HDEVINFO() + { + return handle; + } - if (!OpenProcessToken(winlogon_process, TOKEN_IMPERSONATE | TOKEN_DUPLICATE, &winlogon_token)) - { - CloseHandle(winlogon_process); - RevertToSelf(); - return INVALID_HANDLE_VALUE; - } - CloseHandle(winlogon_process); + ~DevInfoSetHelper() + { + if (is_valid()) + { + SetupDiDestroyDeviceInfoList(handle); + } + } - if (!DuplicateToken(winlogon_token, SecurityImpersonation, &duplicated_token)) - { - CloseHandle(winlogon_token); - RevertToSelf(); - return INVALID_HANDLE_VALUE; - } - CloseHandle(winlogon_token); + private: + HDEVINFO handle; + }; - if (!SetThreadToken(NULL, duplicated_token)) - { - CloseHandle(duplicated_token); - RevertToSelf(); - return INVALID_HANDLE_VALUE; - } - CloseHandle(duplicated_token); - } + struct DeviceInstanceIdInterfaceList : public std::vector + { + DeviceInstanceIdInterfaceList() + { + DevInfoSetHelper device_info_set; + if (!device_info_set.is_valid()) + return; + + for (DWORD i = 0;; ++i) + { + SP_DEVINFO_DATA dev_info_data; + ZeroMemory(&dev_info_data, sizeof(SP_DEVINFO_DATA)); + dev_info_data.cbSize = sizeof(SP_DEVINFO_DATA); + BOOL res = SetupDiEnumDeviceInfo(device_info_set, i, &dev_info_data); + if (!res) + { + if (GetLastError() == ERROR_NO_MORE_ITEMS) + break; + else + continue; + } + + Win::RegKey regkey; + *regkey.ref() = SetupDiOpenDevRegKey(device_info_set, &dev_info_data, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_QUERY_VALUE); + if (!regkey.defined()) + continue; + + std::string str_net_cfg_instance_id; + + DWORD size; + LONG status = RegQueryValueExA(regkey(), "NetCfgInstanceId", NULL, NULL, NULL, &size); + if (status != ERROR_SUCCESS) + continue; + BufferAllocatedType buf_net_cfg_inst_id(size, BufferAllocated::CONSTRUCT_ZERO); + + status = RegQueryValueExA(regkey(), "NetCfgInstanceId", NULL, NULL, (LPBYTE)buf_net_cfg_inst_id.data(), &size); + if (status == ERROR_SUCCESS) + { + buf_net_cfg_inst_id.data()[size - 1] = '\0'; + str_net_cfg_instance_id = std::string(buf_net_cfg_inst_id.data()); + } + else + continue; + + res = SetupDiGetDeviceInstanceId(device_info_set, &dev_info_data, NULL, 0, &size); + if (res != FALSE && GetLastError() != ERROR_INSUFFICIENT_BUFFER) + continue; + + BufferAllocatedType buf_dev_inst_id(size, BufferAllocated::CONSTRUCT_ZERO); + if (!SetupDiGetDeviceInstanceId(device_info_set, &dev_info_data, buf_dev_inst_id.data(), size, &size)) + continue; + buf_dev_inst_id.set_size(size); + + ULONG dev_interface_list_size = 0; + CONFIGRET cr = CM_Get_Device_Interface_List_Size(&dev_interface_list_size, + (LPGUID)& GUID_DEVINTERFACE_NET, + buf_dev_inst_id.data(), + CM_GET_DEVICE_INTERFACE_LIST_PRESENT); + + if (cr != CR_SUCCESS) + continue; + + BufferAllocatedType buf_dev_iface_list(dev_interface_list_size, BufferAllocated::CONSTRUCT_ZERO); + cr = CM_Get_Device_Interface_List((LPGUID)& GUID_DEVINTERFACE_NET, buf_dev_inst_id.data(), + buf_dev_iface_list.data(), + dev_interface_list_size, + CM_GET_DEVICE_INTERFACE_LIST_PRESENT); + if (cr != CR_SUCCESS) + continue; + + DeviceInstanceIdInterfacePair pair; + pair.net_cfg_instance_id = str_net_cfg_instance_id; + pair.device_interface_list = std::string(buf_dev_iface_list.data()); + push_back(pair); + } + } + }; // given a TAP GUID, form the pathname of the TAP device node - inline std::string tap_path(const TapNameGuidPair& tap, bool wintun) + inline std::string tap_path(const TapNameGuidPair& tap) { - if (wintun) - return std::string(USERMODEDEVICEDIR) + "WINTUN" + std::to_string(tap.net_luid_index); - else return std::string(USERMODEDEVICEDIR) + tap.guid + std::string(TAP_WIN_SUFFIX); } @@ -440,31 +476,49 @@ namespace openvpn { { Win::ScopedHANDLE hand; + std::unique_ptr inst_id_interface_list; + if (wintun) + inst_id_interface_list.reset(new DeviceInstanceIdInterfaceList()); + // iterate over list of TAP adapters on system for (TapNameGuidPairList::const_iterator i = guids.begin(); i != guids.end(); i++) { const TapNameGuidPair& tap = *i; - const std::string path = tap_path(tap, wintun); - // wintun device can be only opened under LocalSystem account + std::string path; + if (wintun) - impersonate_as_system(); - - hand.reset(::CreateFileA(path.c_str(), - GENERIC_READ | GENERIC_WRITE, - 0, /* was: FILE_SHARE_READ */ - 0, - OPEN_EXISTING, - FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, - 0)); - if (wintun) - RevertToSelf(); - - if (hand.defined()) { - used = tap; - path_opened = path; - break; + for (const auto& inst_id_interface : *inst_id_interface_list) + { + if (inst_id_interface.net_cfg_instance_id != tap.guid) + continue; + + path = inst_id_interface.device_interface_list; + break; + } + } + else + { + path = tap_path(tap); + } + + if (path.length() > 0) + { + hand.reset(::CreateFileA(path.c_str(), + GENERIC_READ | GENERIC_WRITE, + 0, /* was: FILE_SHARE_READ */ + 0, + OPEN_EXISTING, + FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, + 0)); + + if (hand.defined()) + { + used = tap; + path_opened = path; + break; + } } } return hand.release(); diff --git a/openvpn/tun/win/winproxy.hpp b/openvpn/tun/win/winproxy.hpp index 6a1f78e..c1011fa 100644 --- a/openvpn/tun/win/winproxy.hpp +++ b/openvpn/tun/win/winproxy.hpp @@ -43,7 +43,7 @@ namespace openvpn { void set_proxy(bool del) override { - ImpersonateAsUser imp; + Impersonate imp{false}; LONG status; RegKey hkcu; diff --git a/openvpn/win/call.hpp b/openvpn/win/call.hpp index 54c2ee7..2e965e8 100644 --- a/openvpn/win/call.hpp +++ b/openvpn/win/call.hpp @@ -37,6 +37,9 @@ namespace openvpn { OPENVPN_EXCEPTION(win_call); + // console codepage, used to decode output + int console_cp = ::GetOEMCP(); + inline std::string call(const std::string& cmd) { // split command name from args @@ -145,6 +148,13 @@ namespace openvpn { out += std::string(outbuf.get(), 0, dwRead); } + // decode output using console codepage, convert to utf16 + UTF16 utf16output(Win::utf16(out, console_cp)); + + // re-encode utf16 to utf8 + UTF8 utf8output(Win::utf8(utf16output.get())); + out.assign(utf8output.get()); + // wait for child to exit if (::WaitForSingleObject(process_hand(), INFINITE) == WAIT_FAILED) throw win_call("WaitForSingleObject failed on child process handle"); diff --git a/openvpn/win/event.hpp b/openvpn/win/event.hpp new file mode 100644 index 0000000..f51cf06 --- /dev/null +++ b/openvpn/win/event.hpp @@ -0,0 +1,108 @@ +// 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 . +// + +#ifndef OPENVPN_WIN_EVENT_H +#define OPENVPN_WIN_EVENT_H + +#include + +#include + +#include +#include +#include + +namespace openvpn { + namespace Win { + + // Wrap a standard Windows Event object + class Event + { + public: + explicit Event(BOOL manual_reset=TRUE) + { + event.reset(::CreateEvent(NULL, manual_reset, FALSE, NULL)); + if (!event.defined()) + { + const Win::LastError err; + OPENVPN_THROW_EXCEPTION("Win::Event: cannot create Windows event: " << err.message()); + } + } + + std::string duplicate_local() + { + HANDLE new_handle; + if (!::DuplicateHandle(GetCurrentProcess(), + event(), + GetCurrentProcess(), + &new_handle, + 0, + FALSE, + DUPLICATE_SAME_ACCESS)) + { + const Win::LastError err; + OPENVPN_THROW_EXCEPTION("Win::Event: DuplicateHandle failed: " << err.message()); + } + return BufHex::render(new_handle); + } + + void signal_event() + { + if (event.defined()) + { + ::SetEvent(event()); + event.close(); + } + } + + void release_event() + { + event.close(); + } + + HANDLE operator()() const + { + return event(); + } + + void reset(HANDLE h) + { + event.reset(h); + } + + private: + ScopedHANDLE event; + }; + + // Windows event object that automatically signals in the destructor + struct DestroyEvent : public Event + { + ~DestroyEvent() + { + signal_event(); + } + }; + + } +} + +#endif diff --git a/openvpn/win/impersonate.hpp b/openvpn/win/impersonate.hpp index ea7b026..8cf3fd3 100644 --- a/openvpn/win/impersonate.hpp +++ b/openvpn/win/impersonate.hpp @@ -29,19 +29,120 @@ namespace openvpn { namespace Win { - class ImpersonateAsUser { + + class Impersonate + { public: - ImpersonateAsUser() : local_system(is_local_system_()) + explicit Impersonate(bool as_local_system) + : local_system_(is_local_system_()) { - if (local_system) - OPENVPN_LOG("ImpersonateAsUser: running under SYSTEM account, need to impersonate"); + if (as_local_system) + { + if (local_system_) + OPENVPN_LOG("ImpersonateAsSystem: running under SYSTEM account, no need to impersonate"); + else + impersonate_as_local_system(); + } else { - OPENVPN_LOG("ImpersonateAsUser: running under user account, no need to impersonate"); + if (local_system_) + impersonate_as_user(); + else + OPENVPN_LOG("ImpersonateAsUser: running under user account, no need to impersonate"); + } + } + + ~Impersonate() + { + if (impersonated) + { + if (!RevertToSelf()) + { + const Win::LastError err; + OPENVPN_LOG("Impersonate: RevertToSelf() failed: " << err.message()); + } + } + } + + bool is_local_system() const + { + return local_system_; + } + + private: + void impersonate_as_local_system() + { + HANDLE thread_token, process_snapshot, winlogon_process, winlogon_token, duplicated_token, file_handle; + PROCESSENTRY32 entry = {}; + entry.dwSize = sizeof(PROCESSENTRY32); + BOOL ret; + DWORD pid = 0; + TOKEN_PRIVILEGES privileges = {}; + privileges.PrivilegeCount = 1; + privileges.Privileges->Attributes = SE_PRIVILEGE_ENABLED; + + if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &privileges.Privileges[0].Luid)) + return; + + if (!ImpersonateSelf(SecurityImpersonation)) + return; + + impersonated = true; + + if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, FALSE, &thread_token)) + return; + if (!AdjustTokenPrivileges(thread_token, FALSE, &privileges, sizeof(privileges), NULL, NULL)) + { + CloseHandle(thread_token); return; } + CloseHandle(thread_token); - DWORD sessId = WTSGetActiveConsoleSessionId(); + process_snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (process_snapshot == INVALID_HANDLE_VALUE) + return; + + for (ret = Process32First(process_snapshot, &entry); ret; ret = Process32Next(process_snapshot, &entry)) + { + if (!_stricmp(entry.szExeFile, "winlogon.exe")) + { + pid = entry.th32ProcessID; + break; + } + } + CloseHandle(process_snapshot); + if (!pid) + return; + + winlogon_process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid); + if (!winlogon_process) + return; + + if (!OpenProcessToken(winlogon_process, TOKEN_IMPERSONATE | TOKEN_DUPLICATE, &winlogon_token)) + { + CloseHandle(winlogon_process); + return; + } + CloseHandle(winlogon_process); + + if (!DuplicateToken(winlogon_token, SecurityImpersonation, &duplicated_token)) + { + CloseHandle(winlogon_token); + return; + } + CloseHandle(winlogon_token); + + if (!SetThreadToken(NULL, duplicated_token)) + { + CloseHandle(duplicated_token); + return; + } + CloseHandle(duplicated_token); + } + + void impersonate_as_user() + { + DWORD sessId = WTSGetActiveConsoleSessionId(); if (sessId == 0xFFFFFFFF) { const Win::LastError err; @@ -76,23 +177,6 @@ namespace openvpn { OPENVPN_LOG("ImpersonateAsUser: impersonated as " << uname); } - ~ImpersonateAsUser() { - if (impersonated) - { - if (!RevertToSelf()) - { - const Win::LastError err; - OPENVPN_LOG("ImpersonateAsUser: RevertToSelf() failed: " << err.message()); - } - } - } - - bool is_local_system() const - { - return local_system; - } - - private: // https://stackoverflow.com/a/4024388/227024 BOOL is_local_system_() const { @@ -129,8 +213,8 @@ namespace openvpn { return bSystem; } + bool local_system_ = false; bool impersonated = false; - bool local_system = false; }; } } diff --git a/openvpn/win/scoped_handle.hpp b/openvpn/win/scoped_handle.hpp index bc67999..eb92f93 100644 --- a/openvpn/win/scoped_handle.hpp +++ b/openvpn/win/scoped_handle.hpp @@ -87,10 +87,17 @@ namespace openvpn { { if (defined()) { - const BOOL ret = ::CloseHandle(handle); - //OPENVPN_LOG("**** SH CLOSE hand=" << handle << " ret=" << ret); - handle = nullptr; - return ret != 0; + __try + { + const BOOL ret = ::CloseHandle(handle); + //OPENVPN_LOG("**** SH CLOSE hand=" << handle << " ret=" << ret); + handle = nullptr; + return ret != 0; + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + return false; + } } else return true; diff --git a/openvpn/win/unicode.hpp b/openvpn/win/unicode.hpp index 0dd9a19..7e0b764 100644 --- a/openvpn/win/unicode.hpp +++ b/openvpn/win/unicode.hpp @@ -33,13 +33,14 @@ namespace openvpn { namespace Win { typedef std::unique_ptr UTF16; + typedef std::unique_ptr UTF8; OPENVPN_SIMPLE_EXCEPTION(win_utf16); - inline wchar_t* utf16(const std::string& str) + inline wchar_t* utf16(const std::string& str, int cp=CP_UTF8) { // first get output length (return value includes space for trailing nul) - const int len = ::MultiByteToWideChar(CP_UTF8, + const int len = ::MultiByteToWideChar(cp, 0, str.c_str(), -1, @@ -48,7 +49,7 @@ namespace openvpn { if (len <= 0) throw win_utf16(); UTF16 ret(new wchar_t[len]); - const int len2 = ::MultiByteToWideChar(CP_UTF8, + const int len2 = ::MultiByteToWideChar(cp, 0, str.c_str(), -1, @@ -63,6 +64,33 @@ namespace openvpn { { return ::wcslen(str); } + + inline char* utf8(wchar_t* str) + { + // first get output length (return value includes space for trailing nul) + const int len = ::WideCharToMultiByte(CP_UTF8, + 0, + str, + -1, + NULL, + 0, + NULL, + NULL); + if (len <= 0) + throw win_utf16(); + UTF8 ret(new char[len]); + const int len2 = ::WideCharToMultiByte(CP_UTF8, + 0, + str, + -1, + ret.get(), + len, + NULL, + NULL); + if (len != len2) + throw win_utf16(); + return ret.release(); + } } } #endif diff --git a/scripts/android/build-all b/scripts/android/build-all deleted file mode 100755 index dfab3af..0000000 --- a/scripts/android/build-all +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env bash -# Build the entire core package as required by Android App -set -e -if [ -z "$O3" ]; then - echo O3 var must point to ovpn3 tree - exit 1 -fi - -[ -z "$ECHO" ] && ECHO=0 - -if [ "$ECHO" -eq 0 ]; then - exec > $O3/android_build.log - exec 2>&1 -fi - -[ -z "$DEP_DIR" ] && export DEP_DIR=${O3}/deps -[ -z "$DL" ] && export DL=~/dl - -mkdir -p $DEP_DIR -mkdir -p $DL - -export NO_MOD_PATH=1 -. $O3/core/vars/android-sdk-path - -echo BUILD ANDROID SDK -$O3/core/scripts/android/build-sdk - -echo BUILD TOOLCHAIN -$O3/core/scripts/android/build-toolchain - -echo BUILD DEPS -cd $DEP_DIR -rm -rf asio* lz4* mbedtls* #lzo* boost* minicrypto openssl* polarssl* snappy* -echo "******* ASIO" -$O3/core/deps/asio/build-asio -echo "******* MBEDTLS" -$O3/core/scripts/android/build-mbedtls -echo "******* LZ4" -$O3/core/scripts/android/build-lz4 - -#echo "******* BOOST" -#$O3/core/scripts/android/build-boost -#echo "******* MINICRYPTO" -#$O3/core/scripts/android/build-minicrypto -#echo "******* OpenSSL" -#$O3/core/scripts/android/build-openssl-small -#echo "******* LZO" -#$O3/core/scripts/android/build-lzo -#echo "******* SNAPPY" -#$O3/core/scripts/android/build-snappy - -echo BUILD CORE LIBRARY -$O3/core/javacli/build-android - -echo DONE. diff --git a/scripts/android/build-boost b/scripts/android/build-boost deleted file mode 100755 index e1536b8..0000000 --- a/scripts/android/build-boost +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env bash -# Note: thread library may fail with PAGE_SIZE undefined. -# The pthread.h header makes reference to the PAGE_SIZE define however -# this only gets defined in which is not pulled in by any -# of the pthread.h includes. A google-suggested fix was to add -# #include , which does indeed work, but may not be the -# correct solution for all cases. -set -e -if [ -z "$O3" ]; then - echo O3 var must point to ovpn3 tree ; exit 1 -fi -if [ -z "$DEP_DIR" ]; then - echo DEP_DIR var must point to ovpn3 dependency tree - exit 1 -fi -cd $DEP_DIR -rm -rf boost -mkdir boost -export LINK_MODE=static -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-lz4 b/scripts/android/build-lz4 deleted file mode 100755 index 113e6d4..0000000 --- a/scripts/android/build-lz4 +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env bash - -set -e -if [ -z "$O3" ]; then - echo O3 var must point to ovpn3 tree - exit 1 -fi -if [ -z "$DEP_DIR" ]; then - echo DEP_DIR var must point to the dependency build directory - exit 1 -fi - -. $O3/core/vars/android-sdk-path - -cd $DEP_DIR -rm -rf lz4 -mkdir lz4 - -TARGETS=${TARGETS:-android-a7a android-a8a android-x86} - -for target in $TARGETS; do - echo '***************' TARGET $target - TARGET=$target $O3/core/deps/lz4/build-lz4 -done -exit 0 diff --git a/scripts/android/build-lzo b/scripts/android/build-lzo deleted file mode 100755 index 2d39e2e..0000000 --- a/scripts/android/build-lzo +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env bash - -set -e -if [ -z "$O3" ]; then - echo O3 var must point to ovpn3 tree ; exit 1 -fi -if [ -z "$DEP_DIR" ]; then - echo DEP_DIR var must point to ovpn3 dependency tree - exit 1 -fi -cd $DEP_DIR - -. $O3/core/vars/android-sdk-path - -rm -rf lzo -mkdir lzo - -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 -exit 0 diff --git a/scripts/android/build-mbedtls b/scripts/android/build-mbedtls deleted file mode 100755 index 7f5cf1a..0000000 --- a/scripts/android/build-mbedtls +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env bash - -set -e -if [ -z "$O3" ]; then - echo O3 var must point to ovpn3 tree - exit 1 -fi - -if [ -z "$DEP_DIR" ]; then - echo DEP_DIR var must point to the dependency build directory - exit 1 -fi - -. $O3/core/vars/android-sdk-path - -cd $DEP_DIR -rm -rf mbedtls -mkdir -p mbedtls - -# disable minicrypto for now -mini=0 - -TARGETS=${TARGETS:-android-a7a android-a8a android-x86} - -for target in $TARGETS; do - echo '***************' TARGET $target - VERBOSE=1 TARGET=$target CMAKE_TARGET=android USE_MINICRYPTO=$mini MINICRYPTO_DIR=$(pwd)/minicrypto/minicrypto-$target $O3/core/deps/mbedtls/build-mbedtls - [ "$ANDROID_DBG_ONLY" = "1" ] && exit -done -exit 0 diff --git a/scripts/android/build-minicrypto b/scripts/android/build-minicrypto deleted file mode 100755 index 3f613fd..0000000 --- a/scripts/android/build-minicrypto +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env bash - -set -e -if [ -z "$O3" ]; then - echo O3 var must point to ovpn3 tree ; exit 1 -fi -if [ -z "$DEP_DIR" ]; then - echo DEP_DIR var must point to ovpn3 dependency tree - exit 1 -fi -cd $DEP_DIR - -. $O3/core/vars/android-sdk-path - -rm -rf minicrypto -mkdir minicrypto - -for target in android-a7a android-a7a-dbg ; do - echo '***************' TARGET $target - TARGET=$target $O3/core/deps/minicrypto/build-minicrypto -done -exit 0 diff --git a/scripts/android/build-openssl-small b/scripts/android/build-openssl-small deleted file mode 100755 index 5fdbcce..0000000 --- a/scripts/android/build-openssl-small +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env bash -set -e -if [ -z "$O3" ]; then - echo O3 var must point to ovpn3 tree ; exit 1 -fi -if [ -z "$DEP_DIR" ]; then - echo DEP_DIR var must point to ovpn3 dependency tree - exit 1 -fi -cd $DEP_DIR -. $O3/core/deps/lib-versions -. $O3/core/vars/android-sdk-path -[ -z "$DL" ] && DL=~/Downloads -rm -rf openssl -mkdir openssl -for TARGET in android-a8a android-a8a-dbg android-a7a android-a7a-dbg android-dbg android ; do - . $O3/core/vars/vars-$TARGET - export OPENSSL=$OPENSSL_VERSION - export DIST=$(pwd)/openssl-$PLATFORM - export BIN=$TC/bin - rm -rf $DIST - rm -rf $OPENSSL - tar xfz $DL/$OPENSSL.tar.gz - pushd $OPENSSL - OSSL_FLAGS="no-shared threads no-idea no-mdc2 no-rc5 no-engine no-comp no-hw no-ssl2 no-ssl3 no-zlib no-rc2 no-cast no-md2 no-ripemd no-camellia no-seed no-krb5 no-socks no-ecdsa no-ec no-ecdh no-md2 no-whirlpool no-dsa no-cms no-jpake no-gost" - #OSSL_FLAGS="no-shared threads no-comp no-zlib" - ./Configure linux-armv4 $OSSL_FLAGS --prefix=$DIST - sed -i "" -e "s|-O3|$LIB_OPT_LEVEL $LIB_FPIC $PLATFORM_FLAGS $OTHER_COMPILER_FLAGS|" Makefile - sed -i "" -e "s|ERR_load_COMP_strings()|//ERR_load_COMP_strings()|" crypto/err/err_all.c - make -j 4 build_libs - touch apps/openssl - touch openssl.pc - touch libcrypto.pc - touch libssl.pc - make install_sw - popd - mv $DIST openssl -done -exit 0 diff --git a/scripts/android/build-polarssl b/scripts/android/build-polarssl deleted file mode 100755 index 9950c9a..0000000 --- a/scripts/android/build-polarssl +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash - -set -e -if [ -z "$O3" ]; then - echo O3 var must point to ovpn3 tree ; exit 1 -fi -if [ -z "$DEP_DIR" ]; then - echo DEP_DIR var must point to ovpn3 dependency tree - exit 1 -fi -cd $DEP_DIR - -. $O3/core/vars/android-sdk-path - -# disable minicrypto for now -mini=0 - -rm -rf polarssl -mkdir polarssl - -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/ - [ "$ANDROID_DBG_ONLY" = "1" ] && exit -done -exit 0 diff --git a/scripts/android/build-sdk b/scripts/android/build-sdk deleted file mode 100755 index 01f1a49..0000000 --- a/scripts/android/build-sdk +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env bash - -set -e -if [ -z "$O3" ]; then - echo O3 var must point to ovpn3 tree - exit 1 -fi - -if [ -z "$DEP_DIR" ]; then - echo DEP_DIR var must point to the dependency build directory - exit 1 -fi - -[ -z "$SDK" ] && export SDK=$DEP_DIR/android-sdk - -if [ -d "$SDK" ]; then - echo "Android SDK already exists at $SDK. Doing only update" -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 - -yes | $SDK/tools/bin/sdkmanager --licenses -$SDK/tools/bin/sdkmanager --update -$SDK/tools/bin/sdkmanager --install 'build-tools;26.0.2' \ - 'ndk-bundle' \ - 'extras;android;m2repository' \ - 'patcher;v4' \ - 'platform-tools' \ - 'platforms;android-26' \ - 'tools' - -exit 0 diff --git a/scripts/android/build-snappy b/scripts/android/build-snappy deleted file mode 100755 index 394a1e8..0000000 --- a/scripts/android/build-snappy +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env bash - -set -e -if [ -z "$O3" ]; then - echo O3 var must point to ovpn3 tree ; exit 1 -fi -if [ -z "$DEP_DIR" ]; then - echo DEP_DIR var must point to ovpn3 dependency tree - exit 1 -fi -cd $DEP_DIR - -. $O3/core/vars/android-sdk-path - -rm -rf snappy -mkdir snappy - -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 -exit 0 diff --git a/scripts/android/build-toolchain b/scripts/android/build-toolchain deleted file mode 100755 index 0f8c613..0000000 --- a/scripts/android/build-toolchain +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env bash -# Build the Android toolchain (run first) -#set -e -if [ -z "$O3" ]; then - echo O3 var must point to ovpn3 tree ; exit 1 -fi -export DEP_DIR=${DEP_DIR:-$HOME/src/android} -export NO_MOD_PATH=1 -. $O3/core/vars/android-sdk-path - -# 64 bit -cd $DEP_DIR -DEST=$(pwd)/tc-arm64 -rm -rf $DEST -ABI=aarch64-linux-android -ABI_VER=4.9 -$NDK/build/tools/make-standalone-toolchain.sh \ - --verbose \ - --toolchain=$ABI-$ABI_VER \ - --stl=gnustl \ - --arch=arm64 \ - --platform=android-21 \ - --install-dir=$DEST -cd $DEST/$ABI/bin -ln -s ../../bin/$ABI-gcc cc -ln -s ../../bin/$ABI-gcc gcc -ln -s ../../bin/$ABI-g++ g++ -ln -s ../../libexec/gcc/$ABI/$ABI_VER.x/cc1 cc1 -ln -s ../../libexec/gcc/$ABI/$ABI_VER.x/cc1plus cc1plus - -# 32 bit -cd $DEP_DIR -DEST=$(pwd)/tc-arm -rm -rf $DEST -ABI=arm-linux-androideabi -ABI_VER=4.9 -$NDK/build/tools/make-standalone-toolchain.sh \ - --verbose \ - --toolchain=$ABI-$ABI_VER \ - --stl=gnustl \ - --arch=arm \ - --platform=android-16 \ - --install-dir=$DEST -cd $DEST/$ABI/bin -ln -s ../../bin/$ABI-gcc cc -ln -s ../../libexec/gcc/$ABI/$ABI_VER.x/cc1 cc1 -ln -s ../../libexec/gcc/$ABI/$ABI_VER.x/cc1plus cc1plus - -# 32 bit x86 for Android emulator -cd $DEP_DIR -DEST=$(pwd)/tc-x86 -rm -rf $DEST -ABI=x86-linux-android -SUB=i686-linux-android -ABI_VER=4.9 -$NDK/build/tools/make-standalone-toolchain.sh \ - --verbose \ - --toolchain=$ABI-$ABI_VER \ - --stl=gnustl \ - --arch=x86 \ - --platform=android-16 \ - --install-dir=$DEST -cd $DEST/$SUB/bin -ln -s ../../bin/$SUB-gcc cc -ln -s ../../libexec/gcc/$SUB/$ABI_VER.x/cc1 cc1 -ln -s ../../libexec/gcc/$SUB/$ABI_VER.x/cc1plus cc1plus diff --git a/scripts/build b/scripts/build index a4d46e6..e2e8a20 100755 --- a/scripts/build +++ b/scripts/build @@ -35,6 +35,7 @@ if [ -z "$1" ]; then echo " MTLS_SYS=1 -- use system mbedTLS" echo " MTLS_PATH=path -- use user specified mbedTLS source folder" echo " MTLS_LIBS=ldflags -- user specific mbedTLS LDFLAGS" + echo " MTLS_DIST=path -- use user-specified mbedTLS distribution" echo " MA_HYBRID=1 -- use mbedTLS/AppleCrypto hybrid" echo " OPENSSL_DIST= -- specify custom OpenSSL build" echo " NOSSL=1 -- don't include OpenSSL" @@ -49,6 +50,7 @@ if [ -z "$1" ]; then echo " LZ4_SYS=1 -- build with system LZ4 compression library" echo " SNAP=1 -- build with Snappy compression library" echo " CITY=1 -- build with Cityhash hash library" + echo " CAP=1 -- build with libcap" echo " VAL=1 -- build with valgrind run-time extensions" echo " JAVA=1 -- build with JVM" echo ' EXTRA_CPP="foo1.cpp foo2.cpp" -- add extra .cpp files' @@ -59,6 +61,18 @@ if [ -z "$1" ]; then exit 1 fi +# determine platform if PROF is set to "auto" +if [ "$PROF" = "auto" ]; then + if [ "$(uname)" == "Darwin" ]; then + export PROF=osx64 + elif [ "$(uname)" == "Linux" ]; then + export PROF=linux + else + echo "PROF=auto : cannot determine platform" + exit 1 + fi +fi + # source vars file if [ "$PROF" ]; then suffix="" @@ -123,7 +137,12 @@ FLAGS="$FLAGS -Wno-sign-compare -Wno-unused-parameter" #fi # MbedTLS -if [ "$MTLS_SYS" = "1" ]; then +if [ -n "$MTLS_DIST" ]; then + CPPFLAGS="$CPPFLAGS -DUSE_MBEDTLS -I$MTLS_DIST/include" + LIBDIRS="$LIBDIRS -L$MTLS_DIST/library" + LIBS="$LIBS -lmbedtls -lmbedx509 -lmbedcrypto" + MTLS=1 +elif [ "$MTLS_SYS" = "1" ]; then CPPFLAGS="$CPPFLAGS -DUSE_MBEDTLS" LIBS="$LIBS -lmbedtls -lmbedx509 -lmbedcrypto" elif [ "$MTLS" = "1" ]; then @@ -257,6 +276,12 @@ if [ "$CITY" = "1" ]; then CPPFLAGS="$CPPFLAGS -DHAVE_CITYHASH" fi +# libcap +if [ "$CAP" = "1" ]; then + LIBS="$LIBS -lcap" + CPPFLAGS="$CPPFLAGS -DHAVE_LIBCAP" +fi + # Valgrind if [ "$VAL" = "1" ]; then CPPFLAGS="$CPPFLAGS -DHAVE_VALGRIND" @@ -330,8 +355,12 @@ fi # release/debug builds if [ "$DEBUG" = "1" ]; then - # debug build - FLAGS="-g $FLAGS" + # build with debug symbols + if [ "$PLATFORM" = "linux" ]; then + FLAGS="-ggdb $FLAGS" + else + FLAGS="-g $FLAGS" + fi else # release build [ "$LTO" = "1" ] && [ "$CLANG" != "1" ] && FLAGS="$FLAGS -flto=4 -Wl,--no-as-needed" diff --git a/test/ovpncli/cli.cpp b/test/ovpncli/cli.cpp index fe07d23..a61fa44 100644 --- a/test/ovpncli/cli.cpp +++ b/test/ovpncli/cli.cpp @@ -78,6 +78,7 @@ #if defined(OPENVPN_PLATFORM_WIN) #include +#include #endif #ifdef USE_NETCFG @@ -942,7 +943,14 @@ int openvpn_client(int argc, char *argv[], const std::string* profile_content) ClientAPI::Config config; config.guiVersion = "cli 1.0"; +#if defined(OPENVPN_PLATFORM_WIN) + int nargs = 0; + auto argvw = CommandLineToArgvW(GetCommandLineW(), &nargs); + UTF8 utf8(Win::utf8(argvw[nargs - 1])); + config.content = read_profile(utf8.get(), profile_content); +#else config.content = read_profile(argv[0], profile_content); +#endif for (int i = 1; i < argc; ++i) { config.content += argv[i]; @@ -975,6 +983,7 @@ int openvpn_client(int argc, char *argv[], const std::string* profile_content) config.gremlinConfig = gremlin; config.info = true; config.wintun = wintun; + config.ssoMethods = "openurl"; #if defined(OPENVPN_OVPNCLI_SINGLE_THREAD) config.clockTickMS = 250; #endif @@ -1173,6 +1182,10 @@ int main(int argc, char *argv[]) LogBaseSimple log; #endif +#if defined(OPENVPN_PLATFORM_WIN) + SetConsoleOutputCP(CP_UTF8); +#endif + try { Client::init_process(); ret = openvpn_client(argc, argv, nullptr); diff --git a/test/ovpncli/go b/test/ovpncli/go index 873cf1f..f88aeb8 100755 --- a/test/ovpncli/go +++ b/test/ovpncli/go @@ -16,6 +16,7 @@ GCC_EXTRA="$GCC_EXTRA -DOPENVPN_SHOW_SESSION_TOKEN" [ "$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" +[ "$MDNC" = "1" ] && GCC_EXTRA="$GCC_EXTRA -DMBEDTLS_DISABLE_NAME_CONSTRAINTS" if [ "$AGENT" = "1" ]; then GCC_EXTRA="$GCC_EXTRA -DOPENVPN_COMMAND_AGENT" fi diff --git a/test/unittests/core_tests.cpp b/test/unittests/core_tests.cpp index 030d9fa..24f7fd7 100644 --- a/test/unittests/core_tests.cpp +++ b/test/unittests/core_tests.cpp @@ -27,6 +27,7 @@ #include #include +#include int main (int argc, char **argv) diff --git a/test/unittests/test_log.cpp b/test/unittests/test_log.cpp index dc2b5b0..8f0a717 100644 --- a/test/unittests/test_log.cpp +++ b/test/unittests/test_log.cpp @@ -27,6 +27,10 @@ // file test_ovpncli and do ALL test that require ovpncli in this file // (or have multiple test suites) +// This file needs to included with OPENVPN_EXTERN still defined otherwise +// the include from ovpncli.cpp breaks with duplicate symbols +#include + #undef OPENVPN_EXTERN #define OPENVPN_EXTERN diff --git a/vars/android-sdk-path b/vars/android-sdk-path deleted file mode 100644 index 61edf99..0000000 --- a/vars/android-sdk-path +++ /dev/null @@ -1,7 +0,0 @@ -# setup PATH for Android SDK and NDK -[ -z "$SDK" ] && export SDK=$DEP_DIR/android-sdk -[ -z "$NDK" ] && export NDK=$SDK/ndk-bundle -if [ "$NO_MOD_PATH" != "1" ]; then - export PATH="$SDK/tools:$SDK/platform-tools:$PATH" -fi -export ANDROID_HOME=$SDK \ No newline at end of file diff --git a/win/build.py b/win/build.py index 47a04dc..b349336 100644 --- a/win/build.py +++ b/win/build.py @@ -80,7 +80,7 @@ def build(parms, srcfile, unit_test=False): if parms.get("USE_OPENSSL"): options['extra_inc'] += ' /DUSE_OPENSSL /I %s' % os.path.join(options['openssl'], 'inc32') - options['extra_lib_path'] += ' /LIBPATH:%s' % os.path.join(options['openssl'], 'out32') + options['extra_lib_path'] += ' /LIBPATH:%s' % os.path.join(options['openssl'], 'out32dll') options['extra_lib'] += ' libeay32.lib ssleay32.lib' else: options['extra_inc'] += ' /DUSE_MBEDTLS /I %s' % os.path.join(options['mbedtls'], 'include') @@ -88,7 +88,7 @@ def build(parms, srcfile, unit_test=False): options['extra_lib'] += ' mbedtls.lib' # build it - vc_cmd(parms, r"cl %(extra_defs)s /DNOMINMAX /bigobj /D_CRT_SECURE_NO_WARNINGS /DUSE_ASIO /DASIO_STANDALONE /DASIO_NO_DEPRECATED /I %(asio)s\asio\include /DHAVE_LZ4 /I %(lz4)s %(extra_inc)s -DTAP_WIN_COMPONENT_ID=%(tap_component_id)s /I %(tap)s /I %(ovpn3)s\core /EHsc %(link_static_dynamic_flags)s /W0 %(dbg_rel_flags)s /nologo %(srcfile)s /link /LIBPATH:%(lz4)s%(extra_lib_path)s lz4.lib%(extra_lib)s ws2_32.lib crypt32.lib iphlpapi.lib winmm.lib user32.lib gdi32.lib advapi32.lib wininet.lib shell32.lib ole32.lib rpcrt4.lib Wtsapi32.lib" % options, arch=os.environ.get("ARCH")) + vc_cmd(parms, r"cl %(extra_defs)s /DNOMINMAX /bigobj /D_CRT_SECURE_NO_WARNINGS /DUSE_ASIO /DASIO_STANDALONE /DASIO_NO_DEPRECATED /I %(asio)s\asio\include /DHAVE_LZ4 /I %(lz4)s %(extra_inc)s -DTAP_WIN_COMPONENT_ID=%(tap_component_id)s /I %(tap)s /I %(ovpn3)s\core /EHsc %(link_static_dynamic_flags)s /W0 %(dbg_rel_flags)s /nologo %(srcfile)s /link /LIBPATH:%(lz4)s%(extra_lib_path)s lz4.lib%(extra_lib)s ws2_32.lib crypt32.lib iphlpapi.lib winmm.lib user32.lib gdi32.lib advapi32.lib wininet.lib shell32.lib ole32.lib rpcrt4.lib Wtsapi32.lib Setupapi.lib Cfgmgr32.lib" % options, arch=os.environ.get("ARCH")) if __name__ == "__main__": import sys diff --git a/win/buildep.py b/win/buildep.py index 401c2f4..eee96f9 100644 --- a/win/buildep.py +++ b/win/buildep.py @@ -149,7 +149,7 @@ def build_openssl(parms): rm(arch_path) os.chdir(dist) - for cmd in ["perl Configure VC-WIN64A", "ms\\do_win64a", "nmake -f ms\\nt.mak"]: + for cmd in ["perl Configure VC-WIN64A", "ms\\do_win64a", "nmake -f ms\\ntdll.mak"]: vc_cmd(parms, cmd) def build_all(parms): diff --git a/win/ovpn3-core.sln b/win/ovpn3-core.sln index 15249a8..49f8954 100644 --- a/win/ovpn3-core.sln +++ b/win/ovpn3-core.sln @@ -10,6 +10,9 @@ Global Debug|ARM = Debug|ARM Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 + DebugAgent|ARM = DebugAgent|ARM + DebugAgent|x64 = DebugAgent|x64 + DebugAgent|x86 = DebugAgent|x86 DebugOpenSSL|ARM = DebugOpenSSL|ARM DebugOpenSSL|x64 = DebugOpenSSL|x64 DebugOpenSSL|x86 = DebugOpenSSL|x86 @@ -25,6 +28,10 @@ Global {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}.DebugAgent|ARM.ActiveCfg = DebugAgent|x64 + {1F891260-2039-494F-9777-EC5166AF31BC}.DebugAgent|x64.ActiveCfg = DebugAgent|x64 + {1F891260-2039-494F-9777-EC5166AF31BC}.DebugAgent|x64.Build.0 = DebugAgent|x64 + {1F891260-2039-494F-9777-EC5166AF31BC}.DebugAgent|x86.ActiveCfg = DebugAgent|x64 {1F891260-2039-494F-9777-EC5166AF31BC}.DebugOpenSSL|ARM.ActiveCfg = DebugOpenSSL|x64 {1F891260-2039-494F-9777-EC5166AF31BC}.DebugOpenSSL|x64.ActiveCfg = DebugOpenSSL|x64 {1F891260-2039-494F-9777-EC5166AF31BC}.DebugOpenSSL|x64.Build.0 = DebugOpenSSL|x64 diff --git a/win/ovpn3-core.vcxproj b/win/ovpn3-core.vcxproj index 06cf127..ba2daca 100644 --- a/win/ovpn3-core.vcxproj +++ b/win/ovpn3-core.vcxproj @@ -1,6 +1,10 @@  + + DebugAgent + x64 + DebugOpenSSL x64 @@ -121,6 +125,7 @@ + @@ -420,16 +425,19 @@ + + + @@ -445,7 +453,7 @@ {1F891260-2039-494F-9777-EC5166AF31BC} ovpn3core - 10.0.17134.0 + 10.0 cli @@ -453,25 +461,31 @@ Application true MultiByte - v141 + v142 + + + Application + true + MultiByte + v142 Application true MultiByte - v141 + v142 Application false - v141 + v142 true MultiByte Application false - v141 + v142 true MultiByte @@ -497,10 +511,30 @@ true $(O3)\deps\amd64\mbedtls\library;$(O3)\deps\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;Wtsapi32.lib + 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;Wtsapi32.lib;setupapi.lib NotSet + + + TurnOffAllWarnings + Disabled + false + _CRT_SECURE_NO_WARNINGS;NOMINMAX;_WIN32_WINNT=0x0600;USE_ASIO;ASIO_STANDALONE;USE_MBEDTLS;HAVE_LZ4;TAP_WIN_COMPONENT_ID=tap0901;OPENVPN_COMMAND_AGENT;HAVE_JSONCPP;OVPNAGENT_DISABLE_PATH_CHECK;%(PreprocessorDefinitions) + $(O3)\deps\amd64\mbedtls\include;$(O3)\deps\amd64\tap-windows\src;$(O3)\deps\amd64\asio\asio\include;$(O3)\deps\amd64\lz4\lib;$(O3)\common;$(O3)\core;$(O3)\deps\amd64\jsoncpp\include;%(AdditionalIncludeDirectories) + false + ProgramDatabase + /bigobj %(AdditionalOptions) + MultiThreadedDebug + + + true + $(O3)\deps\amd64\mbedtls\library;$(O3)\deps\amd64\lz4\lib;$(O3)\deps\amd64\jsoncpp\dist;%(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;Wtsapi32.lib;setupapi.lib;jsoncpp.lib + NotSet + Console + + TurnOffAllWarnings @@ -515,8 +549,8 @@ true - $(O3)\deps\amd64\openssl\out32;$(O3)\deps\amd64\lz4\lib;%(AdditionalLibraryDirectories) - gdi32.lib;user32.lib;ssleay32.lib;libeay32.lib;lz4.lib;fwpuclnt.lib;ws2_32.lib;crypt32.lib;iphlpapi.lib;winmm.lib;advapi32.lib;wininet.lib;shell32.lib;ole32.lib;rpcrt4.lib;Wtsapi32.lib + $(O3)\deps\amd64\openssl\out32dll;$(O3)\deps\amd64\lz4\lib;%(AdditionalLibraryDirectories) + gdi32.lib;user32.lib;ssleay32.lib;libeay32.lib;lz4.lib;fwpuclnt.lib;ws2_32.lib;crypt32.lib;iphlpapi.lib;winmm.lib;advapi32.lib;wininet.lib;shell32.lib;ole32.lib;rpcrt4.lib;Wtsapi32.lib;setupapi.lib NotSet @@ -536,7 +570,7 @@ true true $(O3)\deps\amd64\mbedtls\library;$(O3)\deps\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;Wtsapi32.lib;%(AdditionalDependencies) + 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;Wtsapi32.lib;setupapi.lib;%(AdditionalDependencies) @@ -554,8 +588,8 @@ true true true - $(O3)\deps\amd64\openssl\out32;$(O3)\deps\amd64\lz4\lib;%(AdditionalLibraryDirectories) - ssleay32.lib;libeay32.lib;lz4.lib;fwpuclnt.lib;ws2_32.lib;crypt32.lib;iphlpapi.lib;winmm.lib;advapi32.lib;wininet.lib;shell32.lib;ole32.lib;rpcrt4.lib;Wtsapi32.lib;%(AdditionalDependencies) + $(O3)\deps\amd64\openssl\out32dll;$(O3)\deps\amd64\lz4\lib;%(AdditionalLibraryDirectories) + ssleay32.lib;libeay32.lib;lz4.lib;fwpuclnt.lib;ws2_32.lib;crypt32.lib;iphlpapi.lib;winmm.lib;advapi32.lib;wininet.lib;shell32.lib;ole32.lib;rpcrt4.lib;Wtsapi32.lib;setupapi.lib;%(AdditionalDependencies) diff --git a/win/ovpn3-core.vcxproj.filters b/win/ovpn3-core.vcxproj.filters index a6ce3b7..06fbc96 100644 --- a/win/ovpn3-core.vcxproj.filters +++ b/win/ovpn3-core.vcxproj.filters @@ -428,6 +428,9 @@ + + +