Merge commit '8e87aecebf66f50957e35966c547d77a6fb526ab' into feature/update-dependencies

This commit is contained in:
Sergey Abramchuk
2019-10-12 15:50:03 +03:00
124 changed files with 3202 additions and 1291 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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
-------

View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -268,7 +268,7 @@ namespace openvpn {
}
else
{
a.ver = V4;
a.ver = V6;
a.u.v6 = IPv6::Addr::from_byte_string(bytestr);
}
return a;

View File

@@ -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 {

View File

@@ -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>

View File

@@ -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)

View File

@@ -202,6 +202,8 @@ namespace openvpn {
virtual Type vtype() const = 0;
virtual Status vstatus(const SCNetworkReachabilityFlags flags) const = 0;
virtual ~ReachabilityBase() {}
CF::NetworkReachability reach;
};

View File

@@ -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();

View File

@@ -78,7 +78,6 @@ namespace openvpn {
S_SIGINT
| S_SIGTERM
#ifndef OPENVPN_PLATFORM_WIN
| S_SIGQUIT
| S_SIGHUP
| S_SIGUSR1
| S_SIGUSR2

View File

@@ -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;
};
}

View File

@@ -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;
};
}

View File

@@ -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];
};
}

View File

@@ -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();
}

View File

@@ -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();

View File

@@ -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:

View File

@@ -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
};
}

View File

@@ -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());

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;

View File

@@ -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,

View File

@@ -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;
};
}

View File

@@ -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));
}
}
}

View File

@@ -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
{

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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);
}
}
}

View File

@@ -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;

View File

@@ -56,5 +56,13 @@ namespace openvpn {
{
}
};
struct UMaskDaemon : public UMask
{
UMaskDaemon()
: UMask(S_IWOTH)
{
}
};
}
#endif

View File

@@ -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)

View File

@@ -24,5 +24,5 @@
#pragma once
#ifndef OPENVPN_VERSION
#define OPENVPN_VERSION "3.2 (qa:d87f5bbc04)"
#define OPENVPN_VERSION "3.git:master"
#endif

View File

@@ -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

View File

@@ -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;

View File

@@ -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>
{

View File

@@ -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.
//

View File

@@ -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
}
};

View File

@@ -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",

View File

@@ -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,

View File

@@ -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());

View File

@@ -36,6 +36,7 @@ namespace openvpn {
enum {
ECHO_REQUEST = 128,
ECHO_REPLY = 129,
DEST_UNREACH = 1,
PACKET_TOO_BIG = 2
};

View File

@@ -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;
}
}

View File

@@ -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

View File

@@ -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(),

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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)
{

View File

@@ -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");
}
}

View File

@@ -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

View File

@@ -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));
}
}

View File

@@ -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);
}

View File

@@ -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)
{

View File

@@ -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,
};
}
}

View File

@@ -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

View File

@@ -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.

View File

@@ -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;
}
}

View File

@@ -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,

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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; }

View File

@@ -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() {}
};
}
}

View File

@@ -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;
};
}
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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_;
};
}

View File

@@ -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))

View File

@@ -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;
}
};
}

View File

@@ -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,

View File

@@ -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();
}
});

View File

@@ -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_;

View File

@@ -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;
}
};
}
}

View File

@@ -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;

View File

@@ -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));
}
}

View File

@@ -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,

View File

@@ -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;
};
}
}

View File

@@ -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;
};
}
}

View File

@@ -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();

View File

@@ -43,7 +43,7 @@ namespace openvpn {
void set_proxy(bool del) override
{
ImpersonateAsUser imp;
Impersonate imp{false};
LONG status;
RegKey hkcu;

View File

@@ -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");

View File

@@ -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

View File

@@ -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