mirror of
https://github.com/deneraraujo/OpenVPNAdapter.git
synced 2026-02-11 00:00:08 +08:00
Merge commit '8e87aecebf66f50957e35966c547d77a6fb526ab' into feature/update-dependencies
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
-------
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -172,6 +172,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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 <lev@openvpn.net>
|
||||
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
|
||||
|
||||
|
||||
@@ -268,7 +268,7 @@ namespace openvpn {
|
||||
}
|
||||
else
|
||||
{
|
||||
a.ver = V4;
|
||||
a.ver = V6;
|
||||
a.u.v6 = IPv6::Addr::from_byte_string(bytestr);
|
||||
}
|
||||
return a;
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include <openvpn/common/socktypes.hpp>
|
||||
#include <openvpn/common/ffs.hpp>
|
||||
#include <openvpn/common/hexstr.hpp>
|
||||
#include <openvpn/common/hash.hpp>
|
||||
#include <openvpn/addr/iperr.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include <openvpn/common/socktypes.hpp>
|
||||
#include <openvpn/common/ffs.hpp>
|
||||
#include <openvpn/common/hexstr.hpp>
|
||||
#include <openvpn/common/hash.hpp>
|
||||
#include <openvpn/addr/ipv4.hpp>
|
||||
#include <openvpn/addr/iperr.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)
|
||||
|
||||
@@ -202,6 +202,8 @@ namespace openvpn {
|
||||
virtual Type vtype() const = 0;
|
||||
virtual Status vstatus(const SCNetworkReachabilityFlags flags) const = 0;
|
||||
|
||||
virtual ~ReachabilityBase() {}
|
||||
|
||||
CF::NetworkReachability reach;
|
||||
};
|
||||
|
||||
|
||||
@@ -48,6 +48,12 @@
|
||||
|
||||
namespace openvpn {
|
||||
namespace AsioPolySock {
|
||||
// for shutdown()
|
||||
enum ShutdownFlags {
|
||||
SHUTDOWN_SEND = (1<<0),
|
||||
SHUTDOWN_RECV = (1<<1),
|
||||
};
|
||||
|
||||
class Base : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
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();
|
||||
|
||||
@@ -78,7 +78,6 @@ namespace openvpn {
|
||||
S_SIGINT
|
||||
| S_SIGTERM
|
||||
#ifndef OPENVPN_PLATFORM_WIN
|
||||
| S_SIGQUIT
|
||||
| S_SIGHUP
|
||||
| S_SIGUSR1
|
||||
| S_SIGUSR2
|
||||
|
||||
@@ -35,14 +35,16 @@
|
||||
#include <openvpn/common/binprefix.hpp>
|
||||
#include <openvpn/common/to_string.hpp>
|
||||
#include <openvpn/pki/x509track.hpp>
|
||||
#include <openvpn/ssl/sni_metadata.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
class OpenSSLContext;
|
||||
class MbedTLSContext;
|
||||
|
||||
struct AuthCert : public RC<thread_unsafe_refcount>
|
||||
class AuthCert : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
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> fail;
|
||||
std::unique_ptr<X509Track::Set> x509_track;
|
||||
SNI::Metadata::UPtr sni_metadata;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <openvpn/buffer/buffer.hpp>
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <openvpn/buffer/buffer.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
// constant-length Buffer for writing that cannot be extended
|
||||
template <std::size_t N>
|
||||
class StaticBuffer : public Buffer
|
||||
{
|
||||
public:
|
||||
StaticBuffer()
|
||||
: Buffer(data, N, false)
|
||||
{
|
||||
}
|
||||
|
||||
StaticBuffer(const StaticBuffer&) = delete;
|
||||
StaticBuffer& operator=(const StaticBuffer&) = delete;
|
||||
|
||||
private:
|
||||
unsigned char data[N];
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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<thread_unsafe_refcount>
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
// loose emulation of std::clamp for pre-C++17
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
template <typename T>
|
||||
T clamp(T value, T low, T high)
|
||||
{
|
||||
if (value < low)
|
||||
return low;
|
||||
else if (value > high)
|
||||
return high;
|
||||
else
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -34,17 +34,52 @@
|
||||
#include <openvpn/common/logrotate.hpp>
|
||||
#include <openvpn/common/redir.hpp>
|
||||
#include <openvpn/common/usergroup.hpp>
|
||||
#include <openvpn/common/logsetup.hpp>
|
||||
|
||||
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)
|
||||
|
||||
@@ -34,13 +34,13 @@
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/uniqueptr.hpp>
|
||||
#include <openvpn/common/function.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
OPENVPN_EXCEPTION(enum_dir_error);
|
||||
|
||||
template <typename F>
|
||||
inline bool enum_dir(const std::string& dirname,
|
||||
Function<void(std::string fn)> func)
|
||||
F func)
|
||||
{
|
||||
unique_ptr_del<DIR> dir(::opendir(dirname.c_str()), [](DIR* d) { ::closedir(d); });
|
||||
if (!dir)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -35,6 +35,10 @@
|
||||
#include <openvpn/buffer/bufstr.hpp>
|
||||
#include <openvpn/buffer/buflist.hpp>
|
||||
|
||||
#if defined(OPENVPN_PLATFORM_WIN)
|
||||
#include <openvpn/win/unicode.hpp>
|
||||
#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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <openvpn/common/rc.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
class LogSetup : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<LogSetup> Ptr;
|
||||
|
||||
virtual void reopen() const = 0;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -29,16 +29,23 @@
|
||||
namespace openvpn {
|
||||
namespace MSF {
|
||||
|
||||
template <typename MAP_SET, typename ITERATOR>
|
||||
template <typename ITERATOR>
|
||||
class Iter : public ITERATOR
|
||||
{
|
||||
public:
|
||||
template <typename MAP_SET>
|
||||
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 <typename MAP_SET, typename KEY>
|
||||
inline auto find(MAP_SET& ms, const KEY& k)
|
||||
{
|
||||
return Iter<MAP_SET, decltype(ms.find(k))>(ms, ms.find(k));
|
||||
return Iter<decltype(ms.find(k))>(ms, ms.find(k));
|
||||
}
|
||||
|
||||
// Does key exist in map/set?
|
||||
template <typename MAP_SET, typename KEY>
|
||||
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 <typename ITERATOR>
|
||||
inline auto iter(ITERATOR i)
|
||||
{
|
||||
return Iter<ITERATOR>(std::move(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,6 +196,12 @@ namespace openvpn {
|
||||
return px != rhs.px;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
RCPtr<U> static_pointer_cast() const noexcept
|
||||
{
|
||||
return RCPtr<U>(static_cast<U*>(px));
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
RCPtr<U> dynamic_pointer_cast() const noexcept
|
||||
{
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include <type_traits> // for std::is_nothrow_move_constructible
|
||||
#include <utility>
|
||||
|
||||
#include <openvpn/common/platform.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
@@ -45,10 +46,12 @@
|
||||
#include <openvpn/common/stop.hpp>
|
||||
#include <openvpn/common/environ.hpp>
|
||||
#include <openvpn/common/number.hpp>
|
||||
#include <openvpn/common/signal_name.hpp>
|
||||
#include <openvpn/asio/asiosignal.hpp>
|
||||
#include <openvpn/time/time.hpp>
|
||||
#include <openvpn/time/asiotimer.hpp>
|
||||
#include <openvpn/time/timestr.hpp>
|
||||
#include <openvpn/common/logsetup.hpp>
|
||||
|
||||
#ifdef ASIO_HAS_LOCAL_SOCKETS
|
||||
#include <openvpn/common/scoped_fd.hpp>
|
||||
@@ -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;
|
||||
|
||||
@@ -115,6 +115,12 @@ namespace openvpn {
|
||||
return u.dataz[0];
|
||||
}
|
||||
|
||||
template <typename HASH>
|
||||
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 <typename UNORDERED_MAP>
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <signal.h>
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<std::string> 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;
|
||||
|
||||
@@ -56,5 +56,13 @@ namespace openvpn {
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct UMaskDaemon : public UMask
|
||||
{
|
||||
UMaskDaemon()
|
||||
: UMask(S_IWOTH)
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -24,5 +24,5 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef OPENVPN_VERSION
|
||||
#define OPENVPN_VERSION "3.2 (qa:d87f5bbc04)"
|
||||
#define OPENVPN_VERSION "3.git:master"
|
||||
#endif
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<thread_unsafe_refcount>
|
||||
{
|
||||
|
||||
@@ -33,6 +33,9 @@
|
||||
#include <openvpn/ssl/sslchoose.hpp>
|
||||
|
||||
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.
|
||||
//
|
||||
|
||||
@@ -41,11 +41,9 @@
|
||||
#include <openvpn/kovpn/korekey.hpp>
|
||||
#include <openvpn/kovpn/kostats.hpp>
|
||||
#include <openvpn/linux/procfs.hpp>
|
||||
#include <openvpn/dco/ipcollbase.hpp>
|
||||
|
||||
#ifdef ENABLE_PG
|
||||
#include <openvpn/kovpn/kodevtun.hpp>
|
||||
#include <openvpn/kovpn/ipcoll.hpp>
|
||||
#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<decltype(trunk_unit)>("trunk-unit", 1, trunk_unit, 0, 511);
|
||||
|
||||
// parse ping-restart-override
|
||||
ping_restart_override = opt.get_num<decltype(ping_restart_override)>("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<IP::Route> 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<IP::Route> 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<UDP*>(proto.get());
|
||||
}
|
||||
|
||||
static void add_vpn_ips_as_source_routes(const TunBuilderCapture& pull,
|
||||
std::vector<IP::Route>& 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
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -36,6 +36,7 @@ namespace openvpn {
|
||||
enum {
|
||||
ECHO_REQUEST = 128,
|
||||
ECHO_REPLY = 129,
|
||||
DEST_UNREACH = 1,
|
||||
PACKET_TOO_BIG = 2
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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
|
||||
#endif
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include <openvpn/crypto/static_key.hpp>
|
||||
#include <openvpn/crypto/cryptoalgs.hpp>
|
||||
#include <openvpn/openssl/util/error.hpp>
|
||||
#include <openvpn/openssl/compat.hpp>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 <string>
|
||||
#include <vector>
|
||||
@@ -32,16 +31,18 @@
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/openssl/util/error.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace OpenSSLPKI {
|
||||
|
||||
class CRL : public RC<thread_unsafe_refcount>
|
||||
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<char *>(crl_txt.c_str()), crl_txt.length());
|
||||
BIO *bio = ::BIO_new_mem_buf(const_cast<char *>(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<X509_CRL *>(crl));
|
||||
}
|
||||
return ::X509_CRL_dup(const_cast<X509_CRL *>(crl));
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void assign(const X509_CRL *crl)
|
||||
{
|
||||
erase();
|
||||
crl_ = dup(crl);
|
||||
}
|
||||
|
||||
X509_CRL *crl_;
|
||||
::X509_CRL *crl_;
|
||||
};
|
||||
|
||||
typedef RCPtr<CRL> CRLPtr;
|
||||
|
||||
class CRLList : public std::vector<CRLPtr>
|
||||
class CRLList : public std::vector<CRL>
|
||||
{
|
||||
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
|
||||
}
|
||||
|
||||
@@ -21,8 +21,7 @@
|
||||
|
||||
// Wrap an OpenSSL DH object
|
||||
|
||||
#ifndef OPENVPN_OPENSSL_PKI_DH_H
|
||||
#define OPENVPN_OPENSSL_PKI_DH_H
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
@@ -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<char *>(dh_txt.c_str()), dh_txt.length());
|
||||
BIO *bio = ::BIO_new_mem_buf(const_cast<char *>(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
|
||||
|
||||
}
|
||||
|
||||
@@ -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 <string>
|
||||
#include <utility>
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/bio.h>
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/openssl/util/error.hpp>
|
||||
#include <openvpn/pki/pktype.hpp>
|
||||
|
||||
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<char *>(pkey_txt.c_str()), pkey_txt.length());
|
||||
BIO *bio = ::BIO_new_mem_buf(const_cast<char *>(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<EVP_PKEY *>(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
|
||||
}
|
||||
|
||||
@@ -21,8 +21,7 @@
|
||||
|
||||
// Wrap an OpenSSL X509 object
|
||||
|
||||
#ifndef OPENVPN_OPENSSL_PKI_X509_H
|
||||
#define OPENVPN_OPENSSL_PKI_X509_H
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -32,89 +31,77 @@
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/openssl/util/error.hpp>
|
||||
|
||||
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<thread_unsafe_refcount>
|
||||
{
|
||||
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<char *>(cert_txt.c_str()), cert_txt.length());
|
||||
BIO *bio = ::BIO_new_mem_buf(const_cast<char *>(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<X509> X509Ptr;
|
||||
|
||||
class X509List : public std::vector<X509Ptr>
|
||||
class X509List : public std::vector<X509>
|
||||
{
|
||||
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
|
||||
}
|
||||
|
||||
@@ -21,12 +21,10 @@
|
||||
|
||||
// Wrap an OpenSSL X509Store object
|
||||
|
||||
#ifndef OPENVPN_OPENSSL_PKI_X509STORE_H
|
||||
#define OPENVPN_OPENSSL_PKI_X509STORE_H
|
||||
#pragma once
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/pki/cclist.hpp>
|
||||
#include <openvpn/openssl/util/error.hpp>
|
||||
#include <openvpn/openssl/pki/x509.hpp>
|
||||
@@ -35,16 +33,17 @@
|
||||
namespace openvpn {
|
||||
namespace OpenSSLPKI {
|
||||
|
||||
class X509Store : public RC<thread_unsafe_refcount>
|
||||
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<X509List, CRLList> 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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
#include <openvpn/openssl/pki/x509.hpp>
|
||||
#include <openvpn/openssl/util/error.hpp>
|
||||
|
||||
#include <openvpn/openssl/compat.hpp>
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,8 @@
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/x509v3.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/dsa.h>
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/rand.h>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
@@ -44,6 +46,7 @@
|
||||
#include <openvpn/common/uniqueptr.hpp>
|
||||
#include <openvpn/common/hexstr.hpp>
|
||||
#include <openvpn/common/to_string.hpp>
|
||||
#include <openvpn/common/unicode.hpp>
|
||||
#include <openvpn/frame/frame.hpp>
|
||||
#include <openvpn/buffer/buffer.hpp>
|
||||
#include <openvpn/pki/cclist.hpp>
|
||||
@@ -55,6 +58,7 @@
|
||||
#include <openvpn/ssl/sslconsts.hpp>
|
||||
#include <openvpn/ssl/sslapi.hpp>
|
||||
#include <openvpn/ssl/ssllog.hpp>
|
||||
#include <openvpn/ssl/sni_handler.hpp>
|
||||
#include <openvpn/openssl/util/error.hpp>
|
||||
#include <openvpn/openssl/pki/x509.hpp>
|
||||
#include <openvpn/openssl/pki/crl.hpp>
|
||||
@@ -64,6 +68,10 @@
|
||||
#include <openvpn/openssl/bio/bio_memq_stream.hpp>
|
||||
#include <openvpn/openssl/ssl/sess_cache.hpp>
|
||||
|
||||
#ifdef HAVE_JSON
|
||||
#include <openvpn/common/jsonhelper.hpp>
|
||||
#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<std::string> 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<unsigned int> 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<SSL> 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<OpenSSLContext>();
|
||||
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
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
// seed OpenSSL's random number generator with /dev/urandom
|
||||
|
||||
#include <openssl/rand.h>
|
||||
|
||||
#include <openvpn/random/devurand.hpp>
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 <typename CertList, typename CRLList>
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
// private key types
|
||||
|
||||
namespace openvpn {
|
||||
namespace PKType {
|
||||
|
||||
enum Type {
|
||||
PK_UNKNOWN = 0,
|
||||
PK_NONE,
|
||||
PK_DSA,
|
||||
PK_RSA,
|
||||
PK_EC,
|
||||
PK_ECDSA,
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -25,10 +25,12 @@
|
||||
#define OPENVPN_MBEDTLS_UTIL_RANDAPI_H
|
||||
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/random/randistrib.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
@@ -94,6 +96,41 @@ namespace openvpn {
|
||||
return start + rand_get_positive<T>() % (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.
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<unsigned int>& 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<unsigned int>& 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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#include <openvpn/ssl/sslapi.hpp>
|
||||
#include <openvpn/ssl/sni_metadata.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace SNI {
|
||||
|
||||
// Abstract base class used to provide an SNI handler
|
||||
class HandlerBase
|
||||
{
|
||||
public:
|
||||
typedef std::unique_ptr<HandlerBase> 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() {}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
class AuthCert;
|
||||
|
||||
namespace SNI {
|
||||
|
||||
class Metadata
|
||||
{
|
||||
public:
|
||||
typedef std::unique_ptr<Metadata> UPtr;
|
||||
|
||||
virtual std::string sni_client_name(const AuthCert& ac) const = 0;
|
||||
|
||||
virtual ~Metadata() = default;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -32,10 +32,12 @@
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/common/options.hpp>
|
||||
#include <openvpn/common/mode.hpp>
|
||||
#include <openvpn/common/jsonlib.hpp>
|
||||
#include <openvpn/buffer/buffer.hpp>
|
||||
#include <openvpn/frame/frame.hpp>
|
||||
#include <openvpn/auth/authcert.hpp>
|
||||
#include <openvpn/pki/epkibase.hpp>
|
||||
#include <openvpn/pki/pktype.hpp>
|
||||
#include <openvpn/ssl/kuparse.hpp>
|
||||
#include <openvpn/ssl/nscert.hpp>
|
||||
#include <openvpn/ssl/tlsver.hpp>
|
||||
@@ -46,6 +48,10 @@
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
namespace SNI {
|
||||
class HandlerBase;
|
||||
}
|
||||
|
||||
class SSLAPI : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
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<SSLConfigAPI> 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<std::string> 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
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
#include <openvpn/mbedtls/crypto/api.hpp>
|
||||
#include <openvpn/mbedtls/ssl/sslctx.hpp>
|
||||
#include <openvpn/mbedtls/util/rand.hpp>
|
||||
#ifdef HAVE_OPENVPN_COMMON
|
||||
#if defined(HAVE_OPENVPN_COMMON) && !defined(MBEDTLS_DISABLE_NAME_CONSTRAINTS)
|
||||
#include <openvpn/mbedtls/ssl/sslctxnc.hpp>
|
||||
#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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -21,58 +21,71 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/time/asiotimer.hpp>
|
||||
|
||||
// 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 <typename F>
|
||||
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<thread_unsafe_refcount>
|
||||
{
|
||||
typedef RCPtr<Epoch> 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_;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <openvpn/time/time.hpp>
|
||||
#include <openvpn/random/randapi.hpp>
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/common/socktypes.hpp>
|
||||
#include <openvpn/error/excode.hpp>
|
||||
#include <openvpn/frame/frame.hpp>
|
||||
#include <openvpn/log/sessionstats.hpp>
|
||||
#include <openvpn/transport/tcplinkbase.hpp>
|
||||
@@ -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<const ExceptionCode *>(&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();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace openvpn {
|
||||
|
||||
// TunPersistTemplate adds persistence capabilities onto TunWrapTemplate,
|
||||
// in order to implement logic for the persist-tun directive.
|
||||
template <typename SCOPED_OBJ>
|
||||
template <typename SCOPED_OBJ, typename STATE=TunProp::State::Ptr>
|
||||
class TunPersistTemplate : public TunWrapTemplate<SCOPED_OBJ>
|
||||
{
|
||||
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_;
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <openvpn/asio/scoped_asio_stream.hpp>
|
||||
#include <openvpn/tun/client/tunbase.hpp>
|
||||
#include <openvpn/tun/client/tunprop.hpp>
|
||||
#include <openvpn/tun/persist/tunpersist.hpp>
|
||||
#include <openvpn/tun/win/client/tunsetup.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace TunWin {
|
||||
|
||||
// These types manage the underlying TAP driver HANDLE
|
||||
typedef openvpn_io::windows::stream_handle TAPStream;
|
||||
typedef ScopedAsioStream<TAPStream> ScopedTAPStream;
|
||||
struct TunPersistState {
|
||||
TunProp::State::Ptr state;
|
||||
RingBuffer::Ptr ring_buffer;
|
||||
|
||||
void reset()
|
||||
{
|
||||
state.reset();
|
||||
ring_buffer.reset();
|
||||
}
|
||||
};
|
||||
typedef TunPersistTemplate<ScopedTAPStream, TunPersistState> TunPersist;
|
||||
|
||||
class ClientConfig : public TunClientFactory
|
||||
{
|
||||
friend class Client; // accesses wfp
|
||||
|
||||
public:
|
||||
typedef RCPtr<ClientConfig> 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;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,8 @@
|
||||
#include <openvpn/common/stop.hpp>
|
||||
#include <openvpn/tun/builder/capture.hpp>
|
||||
|
||||
#include <openvpn/tun/win/ringbuffer.hpp>
|
||||
|
||||
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;
|
||||
|
||||
|
||||
@@ -38,12 +38,10 @@
|
||||
#include <openvpn/tun/persist/tunpersist.hpp>
|
||||
#include <openvpn/tun/persist/tunwrapasio.hpp>
|
||||
#include <openvpn/tun/tunio.hpp>
|
||||
#include <openvpn/tun/win/client/clientconfig.hpp>
|
||||
#include <openvpn/tun/win/client/tunsetup.hpp>
|
||||
#include <openvpn/win/modname.hpp>
|
||||
|
||||
#define OPENVPN_WINTUN_START_PADDING_LEN 12
|
||||
#define OPENVPN_WINTUN_PACKET_SIZE_LEN 4
|
||||
#define OPENVPN_WINTUN_PACKET_ALIGN 16
|
||||
#include <openvpn/tun/win/client/wintun.hpp>
|
||||
|
||||
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<TAPStream> ScopedTAPStream;
|
||||
typedef TunPersistTemplate<ScopedTAPStream> TunPersist;
|
||||
|
||||
class ClientConfig : public TunClientFactory
|
||||
{
|
||||
friend class Client; // accesses wfp
|
||||
|
||||
public:
|
||||
typedef RCPtr<ClientConfig> 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<DHCPCapture> 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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -0,0 +1,318 @@
|
||||
#pragma once
|
||||
|
||||
#include <openvpn/tun/client/tunbase.hpp>
|
||||
#include <openvpn/tun/persist/tunpersist.hpp>
|
||||
#include <openvpn/tun/win/client/setupbase.hpp>
|
||||
#include <openvpn/tun/win/client/clientconfig.hpp>
|
||||
#include <openvpn/win/modname.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace TunWin {
|
||||
|
||||
class WintunClient : public TunClient
|
||||
{
|
||||
typedef RCPtr<WintunClient> 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<const ExceptionCode*>(&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;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
#include <openvpn/buffer/bufhex.hpp>
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/win/scoped_handle.hpp>
|
||||
#include <openvpn/win/event.hpp>
|
||||
|
||||
#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<thread_unsafe_refcount>
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<RingBuffer> 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<HANDLE>(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<HANDLE>(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<HANDLE>(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<HANDLE>(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;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,12 @@
|
||||
#include <ws2tcpip.h> // for IPv6
|
||||
#include <tlhelp32.h> // for impersonating as LocalSystem
|
||||
|
||||
|
||||
#include <SetupAPI.h>
|
||||
#include <devguid.h>
|
||||
#include <cfgmgr32.h>
|
||||
#include <ndisguid.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
@@ -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<DeviceInstanceIdInterfacePair>
|
||||
{
|
||||
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<char, thread_unsafe_refcount> 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<char, thread_unsafe_refcount> 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<char, thread_unsafe_refcount> 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<DeviceInstanceIdInterfaceList> 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();
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace openvpn {
|
||||
|
||||
void set_proxy(bool del) override
|
||||
{
|
||||
ImpersonateAsUser imp;
|
||||
Impersonate imp{false};
|
||||
|
||||
LONG status;
|
||||
RegKey hkcu;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
#ifndef OPENVPN_WIN_EVENT_H
|
||||
#define OPENVPN_WIN_EVENT_H
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <openvpn/buffer/bufhex.hpp>
|
||||
#include <openvpn/win/winerr.hpp>
|
||||
#include <openvpn/win/scoped_handle.hpp>
|
||||
|
||||
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
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user