mirror of
https://github.com/deneraraujo/OpenVPNAdapter.git
synced 2026-07-08 00:00:02 +08:00
Squashed 'Sources/OpenVPNAdapter/Libraries/Vendors/openvpn/' changes from daf575ff50..275cf80efb
275cf80efb mac/tuncli: Don't take address of temporary error. 1406187bfc tun/win/tunutil: Don't auto& a temporary iterator. fe7f984c5d ip/ping6: Use _WIN32, not _MSC_VER (to fix MinGW). 03a906771e win: add OpenSSL as solution configuration 89cc11b300 win: enable building Windows client with OpenSSL febb24e7d9 openssl/compat.hpp: remove functions already defined in OpenSSL 0833eb1f76 linux/tunsetup: Fix missing asio/errinfo declaration d54b742910 linux: Improve cpu_time() using glibc/kernel methods a55fe2b554 tests: Added unit test for linux/cputime.hpp e33a00e6de [OVPN3-431] agent: Wintun support for agent 42592eb1b1 appveyor: initial commit 3e3f2078e6 win: rename env var in project file a2496a3616 Wintun: experimental support 58a7866b45 build script: added OPENSSL_DIST parameter to specify a custom OpenSSL build 288ea0277e OpenSSLContext: SSL_CTX_set_ecdh_auto() becomes a no-op in OpenSSL 1.1, so #ifdef out to avoid compiler warnings 3ef5059fa6 TLSSessionTicketBase: removed the ERROR symbol from a local enum in case it conflicts with a global preprocessor symbol 3364ed76b8 TLSSessionTicketBase: removed trailing comma from Status enum 025c7bad88 mbedtls/sslctx: Fix missing override in virtual methods 6cb3243681 mbedTLS: ssl() method accepting hostname should check if it is null ca31da7d28 bio_memq_stream.hpp: fixed multi-thread race (introduced with OpenSSL 1.1 support) using init_static() approach 2deb402223 OpenSSLContext::tls_ticket_key_callback: get self with SSL_get_ex_data instead of ssl->ctx->app_verify_arg eec139a100 MSF::find: renamed template type names to avoid conflict with preprocessor symbol (ITER) in test/ssl/proto.cpp 1024d37f33 str_neq: fixed bug where neq was not initialized c00b6f6302 Listen::List: refactored and extended expand_ports() 448c549a0b cpu_time(): added bool thread parameter to return CPU time of current thread (instead of process) 868801d7d9 Linux library: added cpu_time() method to return the CPU time of the current process 964d2cd428 SSL layer: added did_full_handshake() method and implemented for OpenSSL dd18d6c806 crypto::str_neq: use atomic_thread_fence(std::memory_order_acq_rel) instead of OPENVPN_COMPILER_FENCE 6a30af9528 OpenSSLSessionCache: use map instead of unordered_map 3ecbcbc81b OptionList: fixed compile errors that occur when get_num<T>() is used with a const type 72e9f858e4 SSL: added SSLConst::PEER_CERT_OPTIONAL flag and implemented for OpenSSL 33f15c8840 OpenSSL: use OPENSSL_VERSION_NUMBER instead of SSLEAY_VERSION_NUMBER cadb712ea9 ProfileMerge: added "static-key" to is_fileref_directive() 85befa316a TLS session tickets: work around an issue in OpenSSL session ticket keying callback f43c4c1440 TLSSessionTicketBase: misc fixes/enhancements c5f4d59d39 OpenSSLContext: added missing X509_free() to rebuild_authcert() 658fcc50eb OptionList: added get_num methods with min/max but no default 162eeaa485 SSL layer: added RFC 5077 TLS session resumption ticket support e0a821ddd6 OpenSSLContext: use C++11 member initializers 1ea5acce3c OpenSSLContext: minor changes to handshake_details() 74c0a4f995 string: added copy_fill() method 3e5921c06d AuthCert: added is_uninitialized() method 3d6b6b2319 library: added convenience method MSF::find() for maps/sets 18f5f4d1b5 SSLConfigAPI: remove set_enable_renegotiation() 18dcfd616c Added crypto::str_neq() function for securely comparing variable-length strings 4fc5725b9e RunContext: added get_servers() method ae22f155fd server: determine when server-side session ID should be preserved on soon-to-be-closed connections 5e34759d50 client: HALT/RESTART message was not properly purging the Session ID when required e1647eb407 Fix builds with GCC 4.8 compilers b55f78dd1d test_sitnl.cpp: account for old iptools output 236d39258b Allow overriding DEP_DIR by environment variable d56e049ea4 Refactor dependencies to be in a cmake script e9dc75ec90 sitnl: add unit tests faad8454be sitnl: pick the best gw by longest prefix and lowest metric dfcc4bc437 [OVPN3-354] cli.cpp: support for round-robin DNS and redirect gw 8a502f3b61 [OVPN3-354] tun linux: support for round-robin DNS and redirect gw c9315c7dc1 gwnetlink.hpp: specify destination when looking for gateway 89f091daf0 sitnl: implement interface filtering when looking for gateway 220de072a2 sitnl: support for multipart messages 5771dfc0ee transport: remove ip_hole_punch API d448b4a7db tun/builder/client.hpp: use "override" method specifier d85e92621d Make reproducible builds possible 7150f72e09 tun: remove code duplications in Linux tun implementations 8112f0cd7c [OVPN3-378] cli: support for TunBuilder API 6f0e9f6388 Fix Asio 0003 patch. 964662bacb Add /bigobj to build.py 74e40a8907 Upgrade ASIO to 0.13.0 a2713ce1f6 PureTLS: enable SNI by default when configuring client 19a44dbdda Merge branch 'qa' a5fdf43726 InitProcess: comment clarification that crypto_init declaration causes SSL library init when instantiated dec3bc140e OpenSSL: Revert a commit that breaks OpenSSL initialization 16a4e3d4a7 [OVPN3-405] asio: A quick fix for incorrect error message encoding aa785c30c1 Fix Base64::UCharWrap compiler warnings 51a1469e6b Merge various fixes 218cfa39cb Explicitly disable TAP support when parsing configurations 3a0e768ecd Explicitly disable any potential TAP support aba98471fc Fix base64 unit test with mbedtls and windows 9f84174f0b Add unit tests for Base64 017bc545ce Add base64 decode for void* data 452a353b2d Fix lzo build script to use it as dependency for the unit tests dfdd528dc1 Convert unit test to Googletest bd9ee482e6 Add copyright header to test_comp 059f20f2b2 Move compression unit test from common to core repository 5a024cde5c Added Snappy corpus for testing compression/decompression. ec4d400933 Add compatibility functions for OpenSSL 1.1.0 9768562a01 OpenSSL 1.1: Add argument to external sign to specify algorithm 1bbd2cc78c OpenSSL 1.1: Replace RSA_F_RSA_EAY_PRIVATE_ENCRYPT with Openssl variant c959a3cff0 OpenSSL 1.1: Replace remaining direct access to members 4307f024ca OpenSSL 1.1: And missing remaining compat implementations 3385c45151 OpenSSL 1.1: Use opaque pointer for HMAC_CTX f29453f4ca OpenSSL 1.1: Add compat includes for HMAC c107a1f6ab OpenSSL 1.1: Remove support for OpenSSL older than 1.0.0 024a10adc2 OpenSSL 1.1: Use EVP_MD_ctx as opaque pointer 35d82906c4 OpenSSL 1.1: Change EVP_CIPHER ctx field to pointer ebf4b7e87d OpenSSL 1.1: Use X509_digest to get certificate digest 7d3e5d02f2 OpenSSL 1.1: Use SSL_get_ex_data instead of direct access 8717f822ca OpenSSL 1.1: Replace ctx->current with X509_STORE_CTX_get_current_cert 67fbe1ab3f OpenSSL 1.1: Use X509_check_purpose to check certificate types 7b5a92d58e OpenSSL 1.1: Change OpenSSL TLS version logic to match mbed TLS c28b7d1893 OpenSSL 1.1: Adjust default OpenSSL cipher suites f108044a09 OpenSSL 1.1: Add defines for TLS 1.3 in tlsver.hpp ee1308b505 OpenSSL 1.1: Replace initialisation of RSA_meth with access method 905d681af1 OpenSSL 1.1: Use standard tls methods cf28e4600c OpenSSL 1.1: Change BIO wrappers around to use access methods 5e6571163d OpenSSL 1.1: Implement compat methods for new BIO methods in 1.0.2 8837539a73 Use std::nothrow as argument for new e6ec025932 Merge branch 'qa' 752a38c067 [OVPN3-397] size.hpp: wrap typedef in guards d4e50f8c54 Merge branch 'qa' d8d14e1991 [UCONNECT-1027] implement ResolveThread and ensure it is properly detachable 525a9a88a6 Merge branch qa 30ea53cb92 Replace custom memcpy implementation de7c672ee7 Workaround for compiler bug in memneq 84fcecd5e7 Fix missing override annotation in udp/tcp/httpcli 1a3a69a496 [UCONNECT-1027] use one AsioWork object for the whole pre-resolve opertation c4cbf93f9b Revert "[UCONNECT-1027] remotelist: create standalone object for resolve thread" 6ef089164e Allow unit tests to be also compiled with mbed TLS and on Windows 7c67bf7f50 Add unit tests for route emulation and establish common test suite 64a7b2f124 Add build file for core unit tests 0a0d080a49 Implement allowing local LAN access 2105b4b7c0 Fix Android route exclusion emulation git-subtree-dir: Sources/OpenVPNAdapter/Libraries/Vendors/openvpn git-subtree-split: 275cf80efb7a08adc920f7ca49075c776e596b08
This commit is contained in:
@@ -22,36 +22,35 @@
|
||||
// Invert a route list. Used to support excluded routes on platforms that
|
||||
// don't support them natively.
|
||||
|
||||
#ifndef OPENVPN_ADDR_ROUTEINV_H
|
||||
#define OPENVPN_ADDR_ROUTEINV_H
|
||||
#pragma once
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/addr/route.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace IP {
|
||||
class RouteInverter : public RouteList
|
||||
class AddressSpaceSplitter : public RouteList
|
||||
{
|
||||
public:
|
||||
OPENVPN_EXCEPTION(route_inverter);
|
||||
OPENVPN_EXCEPTION(address_space_splitter);
|
||||
|
||||
RouteInverter() {}
|
||||
AddressSpaceSplitter() {}
|
||||
|
||||
// NOTE: when passing RouteInverter to this constructor, make sure
|
||||
// NOTE: when passing AddressSpaceSplitter to this constructor, make sure
|
||||
// to static_cast it to RouteList& so as to avoid matching the
|
||||
// default copy constructor.
|
||||
explicit RouteInverter(const RouteList& in)
|
||||
: RouteInverter(in, in.version_mask())
|
||||
explicit AddressSpaceSplitter(const RouteList& in)
|
||||
: AddressSpaceSplitter(in, in.version_mask())
|
||||
{
|
||||
}
|
||||
|
||||
RouteInverter(const RouteList& in, const Addr::VersionMask vermask)
|
||||
AddressSpaceSplitter(const RouteList& in, const Addr::VersionMask vermask)
|
||||
{
|
||||
in.verify_canonical();
|
||||
if (vermask & Addr::V4_MASK)
|
||||
descend(in, Addr::V4, Route(Addr::from_zero(Addr::V4), 0));
|
||||
descend(in, Route(Addr::from_zero(Addr::V4), 0));
|
||||
if (vermask & Addr::V6_MASK)
|
||||
descend(in, Addr::V6, Route(Addr::from_zero(Addr::V6), 0));
|
||||
descend(in, Route(Addr::from_zero(Addr::V6), 0));
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -60,8 +59,16 @@ namespace openvpn {
|
||||
SUBROUTE,
|
||||
LEAF,
|
||||
};
|
||||
|
||||
void descend(const RouteList& in, const Addr::Version ver, const Route& route)
|
||||
/**
|
||||
* This method construct a non-overlapping list of routes spanning the address
|
||||
* space in @param route. The routes are constructed in a way that each
|
||||
* route in the returned list is smaller or equalto each route in
|
||||
* parameter @param in
|
||||
*
|
||||
* @param route The route we currently are looking at and split if it does
|
||||
* not meet the requirements
|
||||
*/
|
||||
void descend(const RouteList& in, const Route& route)
|
||||
{
|
||||
switch (find(in, route))
|
||||
{
|
||||
@@ -70,18 +77,17 @@ namespace openvpn {
|
||||
Route r1, r2;
|
||||
if (route.split(r1, r2))
|
||||
{
|
||||
descend(in, ver, r1);
|
||||
descend(in, ver, r2);
|
||||
descend(in, r1);
|
||||
descend(in, r2);
|
||||
}
|
||||
else
|
||||
push_back(route);
|
||||
break;
|
||||
}
|
||||
case EQUAL:
|
||||
case LEAF:
|
||||
push_back(route);
|
||||
break;
|
||||
case EQUAL:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,14 +98,12 @@ namespace openvpn {
|
||||
{
|
||||
const Route& r = *i;
|
||||
if (route == r)
|
||||
return EQUAL;
|
||||
type = EQUAL;
|
||||
else if (route.contains(r))
|
||||
type = SUBROUTE;
|
||||
return SUBROUTE;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -148,6 +148,11 @@ namespace openvpn {
|
||||
return !cn.empty();
|
||||
}
|
||||
|
||||
bool is_uninitialized() const
|
||||
{
|
||||
return cn.empty() && sn < 0 && !fail;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T issuer_fp_prefix() const
|
||||
{
|
||||
|
||||
@@ -32,13 +32,11 @@
|
||||
|
||||
namespace openvpn {
|
||||
template<typename RESOLVER_TYPE>
|
||||
class AsyncResolvable: public virtual RC<thread_unsafe_refcount>
|
||||
class AsyncResolvable
|
||||
{
|
||||
private:
|
||||
typedef RCPtr<AsyncResolvable> Ptr;
|
||||
|
||||
openvpn_io::io_context& io_context;
|
||||
|
||||
class ResolveThread : public RC<thread_safe_refcount>
|
||||
{
|
||||
friend class AsyncResolvable<RESOLVER_TYPE>;
|
||||
@@ -46,45 +44,82 @@ namespace openvpn {
|
||||
private:
|
||||
typedef RCPtr<ResolveThread> Ptr;
|
||||
|
||||
std::unique_ptr<AsioWork> asio_work;
|
||||
openvpn_io::io_context& io_context;
|
||||
AsyncResolvable<RESOLVER_TYPE> *resolvable;
|
||||
AsyncResolvable<RESOLVER_TYPE> *parent;
|
||||
std::atomic<bool> detached{false};
|
||||
|
||||
ResolveThread(openvpn_io::io_context &io_context_arg,
|
||||
AsyncResolvable<RESOLVER_TYPE> *resolvable_arg,
|
||||
AsyncResolvable<RESOLVER_TYPE> *parent_arg,
|
||||
const std::string& host, const std::string& port)
|
||||
: asio_work(new AsioWork(io_context_arg)),
|
||||
io_context(io_context_arg),
|
||||
resolvable(resolvable_arg)
|
||||
: io_context(io_context_arg),
|
||||
parent(parent_arg)
|
||||
{
|
||||
std::thread resolve_thread([self=Ptr(this), host, port]() {
|
||||
openvpn_io::io_context io_context(1);
|
||||
openvpn_io::error_code error;
|
||||
RESOLVER_TYPE resolver(io_context);
|
||||
typename RESOLVER_TYPE::results_type results;
|
||||
results = resolver.resolve(host, port, error);
|
||||
std::thread t([self=Ptr(this), host, port]()
|
||||
{
|
||||
openvpn_io::io_context io_context(1);
|
||||
openvpn_io::error_code error;
|
||||
RESOLVER_TYPE resolver(io_context);
|
||||
typename RESOLVER_TYPE::results_type results;
|
||||
|
||||
openvpn_io::post(self->io_context, [self, results, error]() {
|
||||
OPENVPN_ASYNC_HANDLER;
|
||||
self->resolvable->resolve_callback(error, results);
|
||||
});
|
||||
|
||||
// the AsioWork can be released now that we have posted
|
||||
// something else to the main io_context queue
|
||||
self->asio_work.reset();
|
||||
});
|
||||
results = resolver.resolve(host, port, error);
|
||||
if (!self->is_detached())
|
||||
{
|
||||
self->post_callback(results, error);
|
||||
}
|
||||
});
|
||||
// detach the thread so that the client won't need to wait for
|
||||
// it to join.
|
||||
resolve_thread.detach();
|
||||
t.detach();
|
||||
}
|
||||
|
||||
void detach()
|
||||
{
|
||||
detached.store(true, std::memory_order_relaxed);
|
||||
parent = nullptr;
|
||||
}
|
||||
|
||||
bool is_detached() const
|
||||
{
|
||||
return detached.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void post_callback(typename RESOLVER_TYPE::results_type results,
|
||||
openvpn_io::error_code error)
|
||||
{
|
||||
openvpn_io::post(io_context, [self=Ptr(this), results, error]()
|
||||
{
|
||||
auto parent = self->parent;
|
||||
if (!self->is_detached() && parent)
|
||||
{
|
||||
self->detach();
|
||||
OPENVPN_ASYNC_HANDLER;
|
||||
parent->resolve_callback(error, results);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
openvpn_io::io_context& io_context;
|
||||
std::unique_ptr<AsioWork> asio_work;
|
||||
typename ResolveThread::Ptr resolve_thread;
|
||||
|
||||
public:
|
||||
AsyncResolvable(openvpn_io::io_context& io_context_arg)
|
||||
: io_context(io_context_arg)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~AsyncResolvable()
|
||||
{
|
||||
if (resolve_thread)
|
||||
{
|
||||
resolve_thread->detach();
|
||||
resolve_thread.reset();
|
||||
}
|
||||
|
||||
async_resolve_cancel();
|
||||
}
|
||||
|
||||
virtual void resolve_callback(const openvpn_io::error_code& error,
|
||||
typename RESOLVER_TYPE::results_type results) = 0;
|
||||
|
||||
@@ -103,17 +138,24 @@ namespace openvpn {
|
||||
// by ASIO would not be feasible as it is not exposed.
|
||||
void async_resolve_name(const std::string& host, const std::string& port)
|
||||
{
|
||||
// there might be nothing else in the main io_context queue
|
||||
// right now, therefore we use AsioWork to prevent the loop
|
||||
// from exiting while we perform the DNS resolution in the
|
||||
// detached thread.
|
||||
typename ResolveThread::Ptr t(new ResolveThread(io_context, this, host, port));
|
||||
resolve_thread.reset(new ResolveThread(io_context, this, host, port));
|
||||
}
|
||||
|
||||
// The core assumes the existence of this method because it is used on
|
||||
// other platforms (i.e. iOS), therefore we must declare it, even if no-op
|
||||
// there might be nothing else in the main io_context queue
|
||||
// right now, therefore we use AsioWork to prevent the loop
|
||||
// from exiting while we perform the DNS resolution in the
|
||||
// detached thread.
|
||||
void async_resolve_lock()
|
||||
{
|
||||
asio_work.reset(new AsioWork(io_context));
|
||||
}
|
||||
|
||||
// to be called by the child class when the core wants to stop
|
||||
// and we don't need to wait for the detached thread any longer.
|
||||
// It simulates a resolve abort
|
||||
void async_resolve_cancel()
|
||||
{
|
||||
asio_work.reset();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -64,6 +64,11 @@ namespace openvpn {
|
||||
});
|
||||
}
|
||||
|
||||
// no-op: needed to provide the same class signature of the ASIO version
|
||||
void async_resolve_lock()
|
||||
{
|
||||
}
|
||||
|
||||
void async_resolve_cancel()
|
||||
{
|
||||
resolver.cancel();
|
||||
|
||||
@@ -142,12 +142,14 @@ namespace openvpn {
|
||||
return did_replace_password_with_session_id;
|
||||
}
|
||||
|
||||
// If we have a saved password that is not a session ID,
|
||||
// restore it and wipe any existing session ID.
|
||||
bool can_retry_auth_with_cached_password()
|
||||
{
|
||||
if (password_save_defined)
|
||||
{
|
||||
password = password_save;
|
||||
password_save = "";
|
||||
password_save.clear();
|
||||
password_save_defined = false;
|
||||
did_replace_password_with_session_id = false;
|
||||
return true;
|
||||
@@ -156,6 +158,15 @@ namespace openvpn {
|
||||
return false;
|
||||
}
|
||||
|
||||
void purge_session_id()
|
||||
{
|
||||
if (!can_retry_auth_with_cached_password())
|
||||
{
|
||||
password.clear();
|
||||
did_replace_password_with_session_id = false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string auth_info() const
|
||||
{
|
||||
std::string ret;
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/tun/client/emuexr.hpp>
|
||||
#include <openvpn/addr/routeinv.hpp>
|
||||
#include <openvpn/addr/addrspacesplit.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
class EmulateExcludeRouteImpl : public EmulateExcludeRoute
|
||||
@@ -36,48 +36,119 @@ namespace openvpn {
|
||||
|
||||
typedef RCPtr<EmulateExcludeRouteImpl> Ptr;
|
||||
|
||||
EmulateExcludeRouteImpl(const bool exclude_server_address)
|
||||
explicit EmulateExcludeRouteImpl(const bool exclude_server_address)
|
||||
: exclude_server_address_(exclude_server_address)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void add_route(const bool add, const IP::Addr& addr, const int prefix_len)
|
||||
void add_route(const bool add, const IP::Addr& addr, const int prefix_len) override
|
||||
{
|
||||
(add ? include : exclude).emplace_back(addr, prefix_len);
|
||||
}
|
||||
|
||||
virtual bool enabled(const IPVerFlags& ipv) const
|
||||
void add_default_routes(bool ipv4, bool ipv6) override
|
||||
{
|
||||
if (ipv4)
|
||||
add_route(true, IP::Addr::from_zero(IP::Addr::V4), 0);
|
||||
if (ipv6)
|
||||
add_route(true, IP::Addr::from_zero(IP::Addr::V6), 0);
|
||||
}
|
||||
|
||||
bool enabled(const IPVerFlags& ipv) const override
|
||||
{
|
||||
return exclude.size() && (ipv.rgv4() || ipv.rgv6());
|
||||
}
|
||||
|
||||
virtual void emulate(TunBuilderBase* tb, IPVerFlags& ipv, const IP::Addr& server_addr) const
|
||||
void emulate(TunBuilderBase* tb, IPVerFlags& ipv, const IP::Addr& server_addr) const override
|
||||
{
|
||||
const unsigned int rg_ver_flags = ipv.rg_ver_flags();
|
||||
if (exclude.size() && rg_ver_flags)
|
||||
const unsigned int ip_ver_flags = ipv.ip_ver_flags();
|
||||
IP::RouteList rl, tempExcludeList;
|
||||
rl.reserve(include.size() + exclude.size());
|
||||
rl.insert(rl.end(), include.begin(), include.end());
|
||||
rl.insert(rl.end(), exclude.begin(), exclude.end());
|
||||
|
||||
// Check if we have to exclude the server, if yes we temporarily add it to the list
|
||||
// of excluded networks as small individual /32 or /128 network
|
||||
const IP::RouteList* excludedRoutes = &exclude;
|
||||
|
||||
if (exclude_server_address_ && (server_addr.version_mask() & ip_ver_flags) &&
|
||||
!exclude.contains(IP::Route(server_addr, server_addr.size())))
|
||||
{
|
||||
IP::RouteList rl;
|
||||
rl.reserve(include.size() + exclude.size());
|
||||
rl.insert(rl.end(), include.begin(), include.end());
|
||||
rl.insert(rl.end(), exclude.begin(), exclude.end());
|
||||
|
||||
if (exclude_server_address_ && (server_addr.version_mask() & rg_ver_flags))
|
||||
rl.emplace_back(server_addr, server_addr.size());
|
||||
|
||||
const IP::RouteInverter ri(rl, rg_ver_flags);
|
||||
OPENVPN_LOG("Exclude routes emulation:\n" << ri);
|
||||
for (IP::RouteInverter::const_iterator i = ri.begin(); i != ri.end(); ++i)
|
||||
{
|
||||
const IP::Route& r = *i;
|
||||
if (!tb->tun_builder_add_route(r.addr.to_string(), r.prefix_len, -1, r.addr.version() == IP::Addr::V6))
|
||||
throw emulate_exclude_route_error("tun_builder_add_route failed");
|
||||
}
|
||||
|
||||
ipv.set_emulate_exclude_routes();
|
||||
rl.emplace_back(server_addr, server_addr.size());
|
||||
// Create a temporary list that includes all the routes + the server
|
||||
tempExcludeList = exclude;
|
||||
tempExcludeList.emplace_back(server_addr, server_addr.size());
|
||||
excludedRoutes = &tempExcludeList;
|
||||
}
|
||||
|
||||
|
||||
if (excludedRoutes->empty())
|
||||
{
|
||||
// Samsung's Android VPN API does different things if you have
|
||||
// 0.0.0.0/0 in the list of installed routes
|
||||
// (even if 0.0.0.0/1 and 128.0.0.0/1 and are present it behaves different)
|
||||
|
||||
// We normally always split the address space, breaking a 0.0.0.0/0 into
|
||||
// smaller routes. If no routes are excluded, we install the original
|
||||
// routes without modifying them
|
||||
|
||||
for (const auto& rt: include)
|
||||
{
|
||||
if (rt.version() & ip_ver_flags)
|
||||
{
|
||||
if (!tb->tun_builder_add_route(rt.addr.to_string(), rt.prefix_len, -1, rt.addr.version() == IP::Addr::V6))
|
||||
throw emulate_exclude_route_error("tun_builder_add_route failed");
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Complete address space (0.0.0.0/0 or ::/0) split into smaller networks
|
||||
// Figure out which parts of this non overlapping address we want to install
|
||||
for (const auto& r: IP::AddressSpaceSplitter(rl, ip_ver_flags))
|
||||
{
|
||||
if (check_route_should_be_installed(r, *excludedRoutes))
|
||||
if (!tb->tun_builder_add_route(r.addr.to_string(), r.prefix_len, -1, r.addr.version() == IP::Addr::V6))
|
||||
throw emulate_exclude_route_error("tun_builder_add_route failed");
|
||||
}
|
||||
|
||||
ipv.set_emulate_exclude_routes();
|
||||
}
|
||||
|
||||
bool check_route_should_be_installed(const IP::Route& r, const IP::RouteList & excludedRoutes) const
|
||||
{
|
||||
// The whole address space was partioned into NON-overlapping routes that
|
||||
// we get one by one with the parameter r.
|
||||
// Therefore we already know that the whole route r either is included or
|
||||
// excluded IPs.
|
||||
// Figure out if this particular route should be installed or not
|
||||
|
||||
IP::Route const* bestroute = nullptr;
|
||||
// Get the best (longest-prefix/smallest) route from included routes that completely
|
||||
// matches this route
|
||||
for (const auto& incRoute: include)
|
||||
{
|
||||
if (incRoute.contains(r))
|
||||
{
|
||||
if (!bestroute || bestroute->prefix_len < incRoute.prefix_len)
|
||||
bestroute = &incRoute;
|
||||
}
|
||||
}
|
||||
|
||||
// No positive route matches the route at all, do not install it
|
||||
if (!bestroute)
|
||||
return false;
|
||||
|
||||
// Check if there is a more specific exclude route
|
||||
for (const auto& exclRoute: excludedRoutes)
|
||||
{
|
||||
if (exclRoute.contains(r) && exclRoute.prefix_len > bestroute->prefix_len)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const bool exclude_server_address_;
|
||||
IP::RouteList include;
|
||||
IP::RouteList exclude;
|
||||
|
||||
@@ -134,6 +134,7 @@ namespace openvpn {
|
||||
bool echo = false;
|
||||
bool info = false;
|
||||
bool tun_persist = false;
|
||||
bool wintun = false;
|
||||
bool google_dns_fallback = false;
|
||||
bool synchronous_dns_lookup = false;
|
||||
std::string private_key_password;
|
||||
@@ -143,6 +144,7 @@ namespace openvpn {
|
||||
bool force_aes_cbc_ciphersuites = false;
|
||||
bool autologin_sessions = false;
|
||||
bool retry_on_auth_failed = false;
|
||||
bool allow_local_lan_access = false;
|
||||
std::string tls_version_min_override;
|
||||
std::string tls_cert_profile_override;
|
||||
PeerInfo::Set::Ptr extra_peer_info;
|
||||
@@ -347,6 +349,7 @@ namespace openvpn {
|
||||
tunconf->builder = config.builder;
|
||||
tunconf->tun_prop.session_name = session_name;
|
||||
tunconf->tun_prop.google_dns_fallback = config.google_dns_fallback;
|
||||
tunconf->tun_prop.allow_local_lan_access = config.allow_local_lan_access;
|
||||
if (tun_mtu)
|
||||
tunconf->tun_prop.mtu = tun_mtu;
|
||||
tunconf->frame = frame;
|
||||
@@ -362,6 +365,9 @@ namespace openvpn {
|
||||
#if defined(OPENVPN_PLATFORM_ANDROID)
|
||||
// Android VPN API doesn't support excluded routes, so we must emulate them
|
||||
tunconf->eer_factory.reset(new EmulateExcludeRouteFactoryImpl(false));
|
||||
#endif
|
||||
#if defined(OPENVPN_PLATFORM_MAC)
|
||||
tunconf->tun_prefix = true;
|
||||
#endif
|
||||
if (config.tun_persist)
|
||||
tunconf->tun_persist.reset(new TunBuilderClient::TunPersist(true, tunconf->retain_sd, config.builder));
|
||||
@@ -418,6 +424,7 @@ namespace openvpn {
|
||||
tunconf->frame = frame;
|
||||
tunconf->stats = cli_stats;
|
||||
tunconf->stop = config.stop;
|
||||
tunconf->wintun = config.wintun;
|
||||
if (config.tun_persist)
|
||||
{
|
||||
tunconf->tun_persist.reset(new TunWin::TunPersist(true, false, nullptr));
|
||||
@@ -440,8 +447,9 @@ namespace openvpn {
|
||||
#endif
|
||||
}
|
||||
|
||||
// verify that tun implementation can handle OSI layer declared by config
|
||||
if (layer == Layer(Layer::OSI_LAYER_2) && !tun_factory->layer_2_supported())
|
||||
// The Core Library itself does not handle TAP/OSI_LAYER_2 currently,
|
||||
// so we bail out early whenever someone tries to use TAP configurations
|
||||
if (layer == Layer(Layer::OSI_LAYER_2))
|
||||
throw ErrorCode(Error::TAP_NOT_SUPPORTED, true, "OSI layer 2 tunnels are not currently supported");
|
||||
|
||||
// server-poll-timeout
|
||||
|
||||
@@ -424,11 +424,6 @@ namespace openvpn {
|
||||
Base::disable_keepalive(keepalive_ping, keepalive_timeout);
|
||||
}
|
||||
|
||||
virtual void ip_hole_punch(const IP::Addr& addr)
|
||||
{
|
||||
tun_factory->ip_hole_punch(addr);
|
||||
}
|
||||
|
||||
virtual void transport_pre_resolve()
|
||||
{
|
||||
ClientEvent::Base::Ptr ev = new ClientEvent::Resolve();
|
||||
@@ -1007,7 +1002,7 @@ namespace openvpn {
|
||||
void process_halt_restart(const ClientHalt& ch)
|
||||
{
|
||||
if (!ch.psid() && creds)
|
||||
creds->can_retry_auth_with_cached_password(); // purge session ID
|
||||
creds->purge_session_id();
|
||||
if (ch.restart())
|
||||
fatal_ = Error::CLIENT_RESTART;
|
||||
else
|
||||
|
||||
@@ -304,6 +304,7 @@ namespace openvpn {
|
||||
notify_callback = notify_callback_arg;
|
||||
remote_list->index.reset();
|
||||
index = 0;
|
||||
async_resolve_lock();
|
||||
next();
|
||||
}
|
||||
else
|
||||
@@ -350,6 +351,7 @@ namespace openvpn {
|
||||
// resolve unless doing so would result in an empty list.
|
||||
// Then call client's callback method.
|
||||
{
|
||||
async_resolve_cancel();
|
||||
NotifyCallback* ncb = notify_callback;
|
||||
if (remote_list->cached_item_exists())
|
||||
remote_list->prune_uncached();
|
||||
|
||||
@@ -35,10 +35,10 @@ namespace openvpn {
|
||||
|
||||
class Base64 {
|
||||
|
||||
class UCharWrap
|
||||
class ConstUCharWrap
|
||||
{
|
||||
public:
|
||||
UCharWrap(const unsigned char *data, size_t size)
|
||||
ConstUCharWrap(const unsigned char *data, size_t size)
|
||||
: data_(data),
|
||||
size_(size)
|
||||
{}
|
||||
@@ -46,11 +46,38 @@ namespace openvpn {
|
||||
size_t size() const { return size_; }
|
||||
unsigned char operator[](const size_t i) const { return data_[i]; }
|
||||
|
||||
|
||||
private:
|
||||
const unsigned char *data_;
|
||||
size_t size_;
|
||||
};
|
||||
|
||||
public:
|
||||
OPENVPN_SIMPLE_EXCEPTION(base64_decode_out_of_bound_error);
|
||||
|
||||
private:
|
||||
// Minimal class to minic the container our decode method expects
|
||||
class UCharWrap
|
||||
{
|
||||
public:
|
||||
UCharWrap(unsigned char *data, size_t size):
|
||||
data(data), size(size), index(0)
|
||||
{
|
||||
}
|
||||
|
||||
void push_back(unsigned char c)
|
||||
{
|
||||
if (index >= size)
|
||||
throw base64_decode_out_of_bound_error();
|
||||
|
||||
data[index++]=c;
|
||||
}
|
||||
|
||||
unsigned char *data;
|
||||
size_t size;
|
||||
size_t index;
|
||||
};
|
||||
|
||||
public:
|
||||
OPENVPN_SIMPLE_EXCEPTION(base64_bad_map);
|
||||
OPENVPN_SIMPLE_EXCEPTION(base64_decode_error);
|
||||
@@ -75,9 +102,9 @@ namespace openvpn {
|
||||
altmap = "+/=";
|
||||
if (std::strlen(altmap) != 3)
|
||||
throw base64_bad_map();
|
||||
enc[62] = altmap[0];
|
||||
enc[63] = altmap[1];
|
||||
equal = altmap[2];
|
||||
enc[62] = (unsigned char)altmap[0];
|
||||
enc[63] = (unsigned char)altmap[1];
|
||||
equal = (unsigned char)altmap[2];
|
||||
}
|
||||
|
||||
// build decoding map
|
||||
@@ -88,7 +115,7 @@ namespace openvpn {
|
||||
const unsigned char c = enc[i];
|
||||
if (c >= 128)
|
||||
throw base64_bad_map();
|
||||
dec[c] = i;
|
||||
dec[c] = (unsigned char)i;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -139,7 +166,22 @@ namespace openvpn {
|
||||
|
||||
std::string encode(const void *data, size_t size) const
|
||||
{
|
||||
return encode(UCharWrap((const unsigned char *)data, size));
|
||||
return encode(ConstUCharWrap((const unsigned char *)data, size));
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes data from the passed string and stores the
|
||||
* result in data
|
||||
* @param data Destination of the decoded data
|
||||
* @param len Length of the region in data
|
||||
* @param str Base64 string to decode
|
||||
* @return Number of bytes written to data
|
||||
*/
|
||||
size_t decode(void *data, size_t len, const std::string& str) const
|
||||
{
|
||||
UCharWrap ret((unsigned char*)data, len);
|
||||
decode(ret, str);
|
||||
return ret.index;
|
||||
}
|
||||
|
||||
std::string decode(const std::string& str) const
|
||||
|
||||
@@ -19,11 +19,15 @@
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef OPENVPN_COMMON_MEMNEQ_H
|
||||
#define OPENVPN_COMMON_MEMNEQ_H
|
||||
#pragma once
|
||||
|
||||
#include <openvpn/common/arch.hpp>
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <atomic>
|
||||
|
||||
#if defined(USE_OPENSSL)
|
||||
#include <openssl/crypto.h>
|
||||
#endif
|
||||
|
||||
// Does this architecture allow efficient unaligned access?
|
||||
|
||||
@@ -60,7 +64,47 @@ extern "C" void _ReadWriteBarrier();
|
||||
|
||||
namespace openvpn {
|
||||
namespace crypto {
|
||||
// Clang on Android armeabi-v7a seems to have a compiler bug that compiles
|
||||
// the function in a segfaulting variant, use an alternative variant of
|
||||
// the function on all 32 bit arm to be safe
|
||||
|
||||
|
||||
/**
|
||||
* memneq - Compare two areas of memory in constant time
|
||||
*
|
||||
* @a: first area of memory
|
||||
* @b: second area of memory
|
||||
* @size: The length of the memory area to compare
|
||||
*
|
||||
* Returns false when data is equal, true otherwise
|
||||
*/
|
||||
inline bool memneq(const void *a, const void *b, size_t size);
|
||||
|
||||
#if defined(__arm__) && defined(USE_OPENSSL) && !defined(__aarch64__)
|
||||
inline bool memneq(const void *a, const void *b, size_t size)
|
||||
{
|
||||
// memcmp does return 0 (=false) when the memory is equal. It normally
|
||||
// returns the position of first mismatch otherwise but the crypto
|
||||
// variants only promise to return something != 0 (=true)
|
||||
return (bool)(CRYPTO_memcmp(a, b, size));
|
||||
}
|
||||
#elif defined(__arm__) && !defined(__aarch64__)
|
||||
inline bool memneq(const void *a, const void *b, size_t size)
|
||||
{
|
||||
// This is inspired by mbedtls' internal safer_memcmp function:
|
||||
const unsigned char *x = (const unsigned char *) a;
|
||||
const unsigned char *y = (const unsigned char *) b;
|
||||
unsigned char diff = 0;
|
||||
|
||||
for(size_t i = 0; i < size; i++ )
|
||||
{
|
||||
unsigned char u = x[i], v = y[i];
|
||||
diff |= u ^ v;
|
||||
}
|
||||
atomic_thread_fence(std::memory_order_release);
|
||||
return bool(diff);
|
||||
}
|
||||
#else
|
||||
#ifdef OPENVPN_HAVE_EFFICIENT_UNALIGNED_ACCESS
|
||||
enum { memneq_unaligned_ok = 1 };
|
||||
typedef size_t memneq_t;
|
||||
@@ -110,7 +154,6 @@ namespace openvpn {
|
||||
OPENVPN_COMPILER_FENCE
|
||||
return bool(neq);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
// 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/>.
|
||||
|
||||
|
||||
// map/set find
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace openvpn {
|
||||
namespace MSF {
|
||||
|
||||
template <typename MAP_SET, typename ITERATOR>
|
||||
class Iter : public ITERATOR
|
||||
{
|
||||
public:
|
||||
Iter(const MAP_SET& ms, ITERATOR&& iter)
|
||||
: ITERATOR(std::move(iter)),
|
||||
exists_(*this != ms.end())
|
||||
{
|
||||
}
|
||||
|
||||
explicit operator bool() const
|
||||
{
|
||||
return exists_;
|
||||
}
|
||||
|
||||
private:
|
||||
bool exists_;
|
||||
};
|
||||
|
||||
// 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()
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -214,14 +214,15 @@ namespace openvpn {
|
||||
template <typename T>
|
||||
T get_num(const size_t idx) const
|
||||
{
|
||||
T n;
|
||||
typedef typename std::remove_const<T>::type T_nonconst;
|
||||
T_nonconst n;
|
||||
const std::string& numstr = get(idx, 64);
|
||||
if (numstr.length() >= 2 && numstr[0] == '0' && numstr[1] == 'x')
|
||||
{
|
||||
if (!parse_hex_number(numstr.substr(2), n))
|
||||
OPENVPN_THROW(option_error, err_ref() << '[' << idx << "] expecting a hex number");
|
||||
}
|
||||
else if (!parse_number<T>(numstr, n))
|
||||
else if (!parse_number<T_nonconst>(numstr, n))
|
||||
OPENVPN_THROW(option_error, err_ref() << '[' << idx << "] must be a number");
|
||||
return n;
|
||||
}
|
||||
@@ -240,7 +241,16 @@ namespace openvpn {
|
||||
{
|
||||
const T ret = get_num<T>(idx, default_value);
|
||||
if (ret != default_value && (ret < min_value || ret > max_value))
|
||||
OPENVPN_THROW(option_error, err_ref() << '[' << idx << "] must be in the range [" << min_value << ',' << max_value << ']');
|
||||
range_error(idx, min_value, max_value);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T get_num(const size_t idx, const T min_value, const T max_value) const
|
||||
{
|
||||
const T ret = get_num<T>(idx);
|
||||
if (ret < min_value || ret > max_value)
|
||||
range_error(idx, min_value, max_value);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -362,6 +372,12 @@ namespace openvpn {
|
||||
from_list(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void range_error(const size_t idx, const T min_value, const T max_value) const
|
||||
{
|
||||
OPENVPN_THROW(option_error, err_ref() << '[' << idx << "] must be in the range [" << min_value << ',' << max_value << ']');
|
||||
}
|
||||
|
||||
volatile mutable bool touched_ = false;
|
||||
std::vector<std::string> data;
|
||||
};
|
||||
@@ -1215,7 +1231,8 @@ namespace openvpn {
|
||||
template <typename T>
|
||||
T get_num(const std::string& name, const size_t idx, const T default_value) const
|
||||
{
|
||||
T n = default_value;
|
||||
typedef typename std::remove_const<T>::type T_nonconst;
|
||||
T_nonconst n = default_value;
|
||||
const Option* o = get_ptr(name);
|
||||
if (o)
|
||||
n = o->get_num<T>(idx, default_value);
|
||||
@@ -1226,13 +1243,21 @@ namespace openvpn {
|
||||
T get_num(const std::string& name, const size_t idx, const T default_value,
|
||||
const T min_value, const T max_value) const
|
||||
{
|
||||
T n = default_value;
|
||||
typedef typename std::remove_const<T>::type T_nonconst;
|
||||
T_nonconst n = default_value;
|
||||
const Option* o = get_ptr(name);
|
||||
if (o)
|
||||
n = o->get_num<T>(idx, default_value, min_value, max_value);
|
||||
return n;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T get_num(const std::string& name, const size_t idx, const T min_value, const T max_value) const
|
||||
{
|
||||
const Option& o = get(name);
|
||||
return o.get_num<T>(idx, min_value, max_value);
|
||||
}
|
||||
|
||||
// Touch an option, if it exists.
|
||||
void touch(const std::string& name) const
|
||||
{
|
||||
|
||||
@@ -166,6 +166,18 @@ namespace openvpn {
|
||||
log_observers.erase(lu);
|
||||
}
|
||||
|
||||
std::vector<typename ServerThread::Ptr> get_servers()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
std::vector<typename ServerThread::Ptr> ret;
|
||||
if (halt)
|
||||
return ret;
|
||||
ret.reserve(servlist.size());
|
||||
for (auto sp : servlist)
|
||||
ret.emplace_back(sp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void enable_log_history()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
|
||||
@@ -29,8 +29,12 @@
|
||||
#include <openvpn/common/platform.hpp>
|
||||
|
||||
#ifdef OPENVPN_PLATFORM_WIN
|
||||
#if !defined(_SSIZE_T_) && !defined(_SSIZE_T_DEFINED)
|
||||
#include <BaseTsd.h>
|
||||
typedef SSIZE_T ssize_t;
|
||||
#define _SSIZE_T_
|
||||
#define _SSIZE_T_DEFINED
|
||||
#endif
|
||||
#else
|
||||
#include <unistd.h> // get ssize_t
|
||||
#endif
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
#include <cctype>
|
||||
#include <algorithm>
|
||||
|
||||
#include <openvpn/common/platform.hpp>
|
||||
#include <openvpn/common/size.hpp>
|
||||
@@ -68,6 +69,18 @@ namespace openvpn {
|
||||
dest[maxlen - 1] = 0;
|
||||
}
|
||||
|
||||
// Copy string to dest, make sure dest is always null terminated,
|
||||
// and fill out trailing chars in dest with '\0' up to dest_size.
|
||||
inline void copy_fill (void *dest, const std::string& src, const size_t dest_size)
|
||||
{
|
||||
if (dest_size > 0)
|
||||
{
|
||||
const size_t ncopy = std::min(dest_size - 1, src.length());
|
||||
std::memcpy(dest, src.c_str(), ncopy);
|
||||
std::memset(static_cast<unsigned char *>(dest) + ncopy, 0, dest_size - ncopy);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool is_true(const std::string& str)
|
||||
{
|
||||
return str == "1" || !strcasecmp(str.c_str(), "true");
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
// 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/>.
|
||||
|
||||
#include <string>
|
||||
#include <atomic>
|
||||
|
||||
namespace openvpn {
|
||||
namespace crypto {
|
||||
|
||||
// Compare strings in a way that is more resistant to timing attacks.
|
||||
// s1 should be the string provided by the user, while s2 is the
|
||||
// "secret" string that we are comparing s1 against.
|
||||
// Our goal is to prevent timing data from leaking info about the
|
||||
// length or content of s2.
|
||||
// https://nachtimwald.com/2017/04/02/constant-time-string-comparison-in-c/
|
||||
inline bool str_neq(const char *s1, const char *s2)
|
||||
{
|
||||
unsigned int neq = 0;
|
||||
size_t i = 0;
|
||||
size_t j = 0;
|
||||
size_t k = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
neq |= s1[i] ^ s2[j];
|
||||
|
||||
if (s1[i] == '\0')
|
||||
break;
|
||||
i++;
|
||||
|
||||
atomic_thread_fence(std::memory_order_acq_rel);
|
||||
if (s2[j] != '\0')
|
||||
j++;
|
||||
atomic_thread_fence(std::memory_order_acq_rel);
|
||||
if (s2[j] == '\0')
|
||||
k++;
|
||||
}
|
||||
atomic_thread_fence(std::memory_order_acq_rel);
|
||||
return bool(neq);
|
||||
}
|
||||
|
||||
inline bool str_neq(const std::string& s1, const std::string& s2)
|
||||
{
|
||||
return str_neq(s1.c_str(), s2.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,8 @@
|
||||
#define OPENVPN_COMPRESS_LZOASYM_IMPL_H
|
||||
|
||||
#include <cstdint> // for std::uint32_t, etc.
|
||||
#include <cstring> // for memcpy, memmove
|
||||
#include <algorithm>
|
||||
|
||||
#include <openvpn/common/size.hpp> // for ssize_t
|
||||
#include <openvpn/common/likely.hpp> // for likely/unlikely
|
||||
@@ -76,28 +78,6 @@ namespace openvpn {
|
||||
return *cptr(p);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T set_mem(void *p, const T value)
|
||||
{
|
||||
typedef volatile T* ptr;
|
||||
*ptr(p) = value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void copy_mem(void *dest, const void *src)
|
||||
{
|
||||
typedef volatile T* ptr;
|
||||
typedef volatile const T* cptr;
|
||||
*ptr(dest) = *cptr(src);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool ptr_aligned_4(const T* a, const T* b)
|
||||
{
|
||||
return ((size_t(a) | size_t(b)) & 3) == 0;
|
||||
}
|
||||
|
||||
|
||||
// take the number of objects difference between two pointers
|
||||
template <typename T>
|
||||
inline size_t ptr_diff(const T* a, const T* b)
|
||||
@@ -113,76 +93,37 @@ namespace openvpn {
|
||||
return get_mem<std::uint16_t>(p);
|
||||
}
|
||||
|
||||
// copy 64 bits
|
||||
inline void copy_64(unsigned char *dest, const unsigned char *src)
|
||||
/**
|
||||
* This function emulates copying bytes one by one from src to dest.
|
||||
* if src+len and dest+len overlap it repeats the non-overlapping
|
||||
* section of src until it copied 'len' bytes
|
||||
*
|
||||
* A slow simple version of this method looks like this:
|
||||
*
|
||||
* do {
|
||||
* *dest++ = *src++;
|
||||
* } while (--len);
|
||||
*
|
||||
* @param src Source of the memory
|
||||
* @param dest Destination of the memory, must be >= src
|
||||
* @param len Number of bytes to copy from src to dest
|
||||
*/
|
||||
inline void incremental_copy(unsigned char* dest, const unsigned char* src, ssize_t len)
|
||||
{
|
||||
// NOTE: assumes that 64-bit machines can do 64-bit unaligned access, and
|
||||
// 32-bit machines can do 32-bit unaligned access.
|
||||
if (sizeof(void *) == 8)
|
||||
{
|
||||
copy_mem<std::uint64_t>(dest, src);
|
||||
}
|
||||
else
|
||||
{
|
||||
copy_mem<std::uint32_t>(dest, src);
|
||||
copy_mem<std::uint32_t>(dest+4, src+4);
|
||||
}
|
||||
}
|
||||
|
||||
// Fast version of incremental copy.
|
||||
// NOTE: we might write up to ten extra bytes after the end of the copy.
|
||||
inline void incremental_copy_fast(unsigned char *dest, const unsigned char *src, ssize_t len)
|
||||
{
|
||||
while (LZOASYM_UNLIKELY(dest - src < 8))
|
||||
{
|
||||
copy_64(dest, src);
|
||||
len -= dest - src;
|
||||
dest += dest - src;
|
||||
}
|
||||
size_t copylen = dest - src;
|
||||
while (len > 0)
|
||||
{
|
||||
copy_64(dest, src);
|
||||
src += 8;
|
||||
dest += 8;
|
||||
len -= 8;
|
||||
memcpy(dest, src, std::min((size_t)len, (size_t)copylen));
|
||||
dest += copylen;
|
||||
len -= copylen;
|
||||
|
||||
/* we can double copylen every time
|
||||
* we copied the pattern */
|
||||
copylen = copylen * 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Slow version of incremental copy
|
||||
inline void incremental_copy(unsigned char *dest, const unsigned char *src, ssize_t len)
|
||||
{
|
||||
do {
|
||||
*dest++ = *src++;
|
||||
} while (--len);
|
||||
}
|
||||
|
||||
// Faster version of memcpy
|
||||
inline void copy_fast(unsigned char *dest, const unsigned char *src, ssize_t len)
|
||||
{
|
||||
while (len >= 8)
|
||||
{
|
||||
copy_64(dest, src);
|
||||
src += 8;
|
||||
dest += 8;
|
||||
len -= 8;
|
||||
}
|
||||
if (len >= 4)
|
||||
{
|
||||
copy_mem<std::uint32_t>(dest, src);
|
||||
src += 4;
|
||||
dest += 4;
|
||||
len -= 4;
|
||||
}
|
||||
switch (len)
|
||||
{
|
||||
case 3:
|
||||
*dest++ = *src++;
|
||||
case 2:
|
||||
*dest++ = *src++;
|
||||
case 1:
|
||||
*dest = *src;
|
||||
}
|
||||
}
|
||||
|
||||
inline int lzo1x_decompress_safe(const unsigned char *input,
|
||||
size_t input_length,
|
||||
@@ -229,7 +170,7 @@ namespace openvpn {
|
||||
const size_t len = z + 3;
|
||||
LZOASYM_CHECK_OUTPUT_OVERFLOW(len);
|
||||
LZOASYM_CHECK_INPUT_OVERFLOW(len+1);
|
||||
copy_fast(output_ptr, input_ptr, len);
|
||||
memcpy(output_ptr, input_ptr, len);
|
||||
input_ptr += len;
|
||||
output_ptr += len;
|
||||
}
|
||||
@@ -322,13 +263,7 @@ namespace openvpn {
|
||||
LZOASYM_CHECK_OUTPUT_OVERFLOW(z+3-1);
|
||||
|
||||
const size_t len = z + 2;
|
||||
// Should we use optimized incremental copy?
|
||||
// incremental_copy_fast might copy 10 more bytes than needed, so
|
||||
// don't use it unless we have enough trailing space in buffer.
|
||||
if (LZOASYM_LIKELY(size_t(output_ptr_end - output_ptr) >= len + 10))
|
||||
incremental_copy_fast(output_ptr, match_ptr, len);
|
||||
else
|
||||
incremental_copy(output_ptr, match_ptr, len);
|
||||
incremental_copy(output_ptr, match_ptr, len);
|
||||
match_ptr += len;
|
||||
output_ptr += len;
|
||||
}
|
||||
|
||||
+12
-13
@@ -34,7 +34,7 @@
|
||||
#include <openvpn/transport/client/transbase.hpp>
|
||||
#include <openvpn/tun/client/tunbase.hpp>
|
||||
#include <openvpn/tun/builder/capture.hpp>
|
||||
#include <openvpn/tun/linux/client/tuncli.hpp>
|
||||
#include <openvpn/tun/linux/client/tuniproute.hpp>
|
||||
#include <openvpn/transport/dco.hpp>
|
||||
#include <openvpn/kovpn/kovpn.hpp>
|
||||
#include <openvpn/kovpn/kodev.hpp>
|
||||
@@ -336,12 +336,12 @@ namespace openvpn {
|
||||
detect_vpn_ip_collision(*vpn_ip_collision, *po, config->trunk_unit, *remove_cmds);
|
||||
|
||||
// trunk setup
|
||||
TunLinux::iface_config(state->iface_name,
|
||||
config->trunk_unit,
|
||||
*po,
|
||||
nullptr,
|
||||
*add_cmds,
|
||||
*remove_cmds);
|
||||
TunIPRoute::iface_config(state->iface_name,
|
||||
config->trunk_unit,
|
||||
*po,
|
||||
nullptr,
|
||||
*add_cmds,
|
||||
*remove_cmds);
|
||||
|
||||
// Note that in trunking mode, kovpn must be
|
||||
// configured for source routing.
|
||||
@@ -351,11 +351,11 @@ namespace openvpn {
|
||||
else
|
||||
{
|
||||
// non-trunk setup
|
||||
TunLinux::tun_config(state->iface_name,
|
||||
*po,
|
||||
&rtvec,
|
||||
*add_cmds,
|
||||
*remove_cmds);
|
||||
TunIPRoute::TunMethods::tun_config(state->iface_name,
|
||||
*po,
|
||||
&rtvec,
|
||||
*add_cmds,
|
||||
*remove_cmds);
|
||||
}
|
||||
|
||||
// Add routes to DCO implementation
|
||||
@@ -533,7 +533,6 @@ namespace openvpn {
|
||||
config->transport.remote_list->get_endpoint(udp().server_endpoint);
|
||||
OPENVPN_LOG("Contacting " << udp().server_endpoint << " via UDP");
|
||||
transport_parent->transport_wait();
|
||||
transport_parent->ip_hole_punch(server_endpoint_addr());
|
||||
udp().socket.open(udp().server_endpoint.protocol());
|
||||
udp().socket.async_connect(udp().server_endpoint, [self=Ptr(this)](const openvpn_io::error_code& error)
|
||||
{
|
||||
|
||||
@@ -46,6 +46,7 @@ namespace openvpn {
|
||||
READ_LINK_UDP,
|
||||
READ_LINK_TCP,
|
||||
READ_TUN,
|
||||
READ_WINTUN,
|
||||
READ_BIO_MEMQ_DGRAM,
|
||||
READ_BIO_MEMQ_STREAM,
|
||||
READ_SSL_CLEARTEXT,
|
||||
|
||||
@@ -52,6 +52,12 @@ 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());
|
||||
|
||||
@@ -59,6 +59,10 @@ namespace openvpn {
|
||||
{
|
||||
base64_uninit_static();
|
||||
}
|
||||
|
||||
private:
|
||||
// SSL library init happens when instantiated
|
||||
crypto_init crypto_init_;
|
||||
};
|
||||
|
||||
// process-wide singular instance
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace openvpn {
|
||||
|
||||
inline static const std::uint16_t* get_addr16(const struct in6_addr *addr)
|
||||
{
|
||||
#if defined(_MSC_VER)
|
||||
#if defined(_WIN32)
|
||||
return addr->u.Word;
|
||||
#elif defined(__APPLE__)
|
||||
return addr->__u6_addr.__u6_addr16;
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
// 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 <errno.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/resource.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <openvpn/common/file.hpp>
|
||||
#include <openvpn/common/string.hpp>
|
||||
#include <openvpn/common/number.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
/**
|
||||
* Retrieve the time (in seconds) the current process or thread
|
||||
* has been running. Runing time includes both system and user
|
||||
* times.
|
||||
*
|
||||
* @param thread Boolean flag controlling if process or thread
|
||||
* runtime should be returned
|
||||
*
|
||||
* @return Returns a double containing number of seconds the
|
||||
* current process (PID) or thread has been running.
|
||||
* On errors -1.0 is returned.
|
||||
*
|
||||
*/
|
||||
inline double cpu_time(const bool thread=false)
|
||||
{
|
||||
try
|
||||
{
|
||||
struct rusage usage;
|
||||
|
||||
if (getrusage((thread ? RUSAGE_THREAD : RUSAGE_SELF), &usage) != 0)
|
||||
{
|
||||
throw Exception("getrusage() call failed: " + std::string(strerror(errno)));
|
||||
}
|
||||
double utime = usage.ru_utime.tv_sec + ((double)usage.ru_utime.tv_usec / 1000000);
|
||||
double stime = usage.ru_stime.tv_sec + ((double)usage.ru_stime.tv_usec / 1000000);
|
||||
|
||||
return utime + stime;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
//OPENVPN_LOG("cpu_time exception: " << e.what());
|
||||
return -1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -212,7 +212,6 @@ namespace openvpn {
|
||||
tls_version_min(TLSVersion::UNDEF),
|
||||
tls_cert_profile(TLSCertProfile::UNDEF),
|
||||
local_cert_enabled(true),
|
||||
enable_renegotiation(false),
|
||||
force_aes_cbc_ciphersuites(false),
|
||||
allow_name_constraints(false) {}
|
||||
|
||||
@@ -237,6 +236,18 @@ namespace openvpn {
|
||||
external_pki = external_pki_arg;
|
||||
}
|
||||
|
||||
virtual void set_session_ticket_handler(TLSSessionTicketBase* session_ticket_handler_arg)
|
||||
{
|
||||
// fixme -- this method should be implemented for server-side TLS session resumption tickets
|
||||
throw MbedTLSException("set_session_ticket_handler not implemented");
|
||||
}
|
||||
|
||||
virtual void set_client_session_tickets(const bool v)
|
||||
{
|
||||
// fixme -- this method should be implemented for client-side TLS session resumption tickets
|
||||
throw MbedTLSException("set_client_session_tickets not implemented");
|
||||
}
|
||||
|
||||
virtual void set_private_key_password(const std::string& pwd)
|
||||
{
|
||||
priv_key_pwd = pwd;
|
||||
@@ -397,11 +408,6 @@ namespace openvpn {
|
||||
local_cert_enabled = v;
|
||||
}
|
||||
|
||||
virtual void set_enable_renegotiation(const bool v)
|
||||
{
|
||||
enable_renegotiation = v;
|
||||
}
|
||||
|
||||
virtual void set_force_aes_cbc_ciphersuites(const bool v)
|
||||
{
|
||||
force_aes_cbc_ciphersuites = v;
|
||||
@@ -590,7 +596,6 @@ namespace openvpn {
|
||||
TLSCertProfile::Type tls_cert_profile;
|
||||
X509Track::ConfigSet x509_track_config;
|
||||
bool local_cert_enabled;
|
||||
bool enable_renegotiation;
|
||||
bool force_aes_cbc_ciphersuites;
|
||||
bool allow_name_constraints;
|
||||
RandomAPI::Ptr rng; // random data source
|
||||
@@ -612,12 +617,12 @@ namespace openvpn {
|
||||
public:
|
||||
typedef RCPtr<SSL> Ptr;
|
||||
|
||||
virtual void start_handshake()
|
||||
virtual void start_handshake() override
|
||||
{
|
||||
mbedtls_ssl_handshake(ssl);
|
||||
}
|
||||
|
||||
virtual ssize_t write_cleartext_unbuffered(const void *data, const size_t size)
|
||||
virtual ssize_t write_cleartext_unbuffered(const void *data, const size_t size) override
|
||||
{
|
||||
const int status = mbedtls_ssl_write(ssl, (const unsigned char*)data, size);
|
||||
if (status < 0)
|
||||
@@ -633,7 +638,7 @@ namespace openvpn {
|
||||
return status;
|
||||
}
|
||||
|
||||
virtual ssize_t read_cleartext(void *data, const size_t capacity)
|
||||
virtual ssize_t read_cleartext(void *data, const size_t capacity) override
|
||||
{
|
||||
if (!overflow)
|
||||
{
|
||||
@@ -656,12 +661,12 @@ namespace openvpn {
|
||||
throw ssl_ciphertext_in_overflow();
|
||||
}
|
||||
|
||||
virtual bool read_cleartext_ready() const
|
||||
virtual bool read_cleartext_ready() const override
|
||||
{
|
||||
return !ct_in.empty() || mbedtls_ssl_get_bytes_avail(ssl);
|
||||
}
|
||||
|
||||
virtual void write_ciphertext(const BufferPtr& buf)
|
||||
virtual void write_ciphertext(const BufferPtr& buf) override
|
||||
{
|
||||
if (ct_in.size() < MAX_CIPHERTEXT_IN)
|
||||
ct_in.write_buf(buf);
|
||||
@@ -669,7 +674,7 @@ namespace openvpn {
|
||||
overflow = true;
|
||||
}
|
||||
|
||||
virtual void write_ciphertext_unbuffered(const unsigned char *data, const size_t size)
|
||||
virtual void write_ciphertext_unbuffered(const unsigned char *data, const size_t size) override
|
||||
{
|
||||
if (ct_in.size() < MAX_CIPHERTEXT_IN)
|
||||
ct_in.write(data, size);
|
||||
@@ -677,17 +682,17 @@ namespace openvpn {
|
||||
overflow = true;
|
||||
}
|
||||
|
||||
virtual bool read_ciphertext_ready() const
|
||||
virtual bool read_ciphertext_ready() const override
|
||||
{
|
||||
return !ct_out.empty();
|
||||
}
|
||||
|
||||
virtual BufferPtr read_ciphertext()
|
||||
virtual BufferPtr read_ciphertext() override
|
||||
{
|
||||
return ct_out.read_buf();
|
||||
}
|
||||
|
||||
virtual std::string ssl_handshake_details() const
|
||||
virtual std::string ssl_handshake_details() const override
|
||||
{
|
||||
if (ssl)
|
||||
{
|
||||
@@ -699,11 +704,21 @@ namespace openvpn {
|
||||
return "";
|
||||
}
|
||||
|
||||
virtual const AuthCert::Ptr& auth_cert() const
|
||||
virtual bool did_full_handshake() override
|
||||
{
|
||||
return false; // fixme -- not implemented
|
||||
}
|
||||
|
||||
virtual const AuthCert::Ptr& auth_cert() override
|
||||
{
|
||||
return authcert;
|
||||
}
|
||||
|
||||
virtual void mark_no_cache() override
|
||||
{
|
||||
// fixme -- this method should be implemented for client-side TLS session resumption tickets
|
||||
}
|
||||
|
||||
virtual ~SSL()
|
||||
{
|
||||
erase();
|
||||
@@ -787,15 +802,12 @@ namespace openvpn {
|
||||
|
||||
// Notes on SSL resume/renegotiation:
|
||||
// SSL resume on server side is controlled by ssl_set_session_cache.
|
||||
// SSL renegotiation on/off is handled here via ssl_set_renegotiation.
|
||||
// Without calling ssl_set_renegotiation, it defaults to
|
||||
// MBEDTLS_SSL_RENEGOTIATION_DISABLED and ssl_legacy_renegotiation defaults to
|
||||
// SSL renegotiation is disabled here via MBEDTLS_SSL_RENEGOTIATION_DISABLED
|
||||
// and ssl_legacy_renegotiation defaults to
|
||||
// MBEDTLS_SSL_LEGACY_NO_RENEGOTIATION. To enable session tickets,
|
||||
// MBEDTLS_SSL_SESSION_TICKETS (compile flag) must be defined
|
||||
// in mbed TLS config.h.
|
||||
mbedtls_ssl_conf_renegotiation(sslconf,
|
||||
c.enable_renegotiation
|
||||
? MBEDTLS_SSL_RENEGOTIATION_ENABLED : MBEDTLS_SSL_RENEGOTIATION_DISABLED);
|
||||
mbedtls_ssl_conf_renegotiation(sslconf, MBEDTLS_SSL_RENEGOTIATION_DISABLED);
|
||||
|
||||
mbedtls_ssl_conf_ciphersuites(sslconf, c.force_aes_cbc_ciphersuites ?
|
||||
mbedtls_ctx_private::aes_cbc_ciphersuites :
|
||||
@@ -982,9 +994,9 @@ namespace openvpn {
|
||||
}
|
||||
|
||||
// like ssl() above but verify hostname against cert CommonName and/or SubjectAltName
|
||||
virtual SSLAPI::Ptr ssl(const std::string& hostname)
|
||||
virtual SSLAPI::Ptr ssl(const std::string* hostname, const std::string* cache_key)
|
||||
{
|
||||
return SSL::Ptr(new SSL(this, hostname.c_str()));
|
||||
return SSL::Ptr(new SSL(this, hostname ? hostname->c_str() : nullptr));
|
||||
}
|
||||
|
||||
virtual const Mode& mode() const
|
||||
@@ -1374,7 +1386,7 @@ namespace openvpn {
|
||||
|
||||
/* get signature */
|
||||
std::string sig_b64;
|
||||
const bool status = self->config->external_pki->sign(from_b64, sig_b64);
|
||||
const bool status = self->config->external_pki->sign(from_b64, sig_b64, "RSA_PKCS1_PADDING");
|
||||
if (!status)
|
||||
throw ssl_external_pki("MbedTLS: could not obtain signature");
|
||||
|
||||
|
||||
@@ -37,7 +37,17 @@ namespace openvpn {
|
||||
public:
|
||||
OPENVPN_EXCEPTION(linux_gw_netlink_error);
|
||||
|
||||
LinuxGWNetlink(bool ipv6)
|
||||
/**
|
||||
* Provides gateway which is used to reach given address
|
||||
*
|
||||
* @param addr address which we want to reach
|
||||
* @param iface_to_ignore this allows to exclude certain interface
|
||||
* from discovered gateways. Used when we want to exclude VPN interface
|
||||
* when there is active VPN connection with redirected default gateway
|
||||
*
|
||||
* @param ipv6 true if address is IPv6
|
||||
*/
|
||||
LinuxGWNetlink(const std::string& addr, const std::string& iface_to_ignore, bool ipv6)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -45,8 +55,8 @@ namespace openvpn {
|
||||
{
|
||||
IPv6::Addr addr6;
|
||||
|
||||
if (TunNetlink::SITNL::net_route_best_gw(IP::Route6(IPv6::Addr::from_zero(), 0),
|
||||
addr6, dev_) < 0)
|
||||
if (TunNetlink::SITNL::net_route_best_gw(IP::Route6::from_string(addr),
|
||||
addr6, dev_, iface_to_ignore) < 0)
|
||||
{
|
||||
OPENVPN_THROW(linux_gw_netlink_error,
|
||||
"error retrieving default IPv6 GW");
|
||||
@@ -58,8 +68,8 @@ namespace openvpn {
|
||||
{
|
||||
IPv4::Addr addr4;
|
||||
|
||||
if (TunNetlink::SITNL::net_route_best_gw(IP::Route4(IPv4::Addr::from_zero(), 0),
|
||||
addr4, dev_) < 0)
|
||||
if (TunNetlink::SITNL::net_route_best_gw(IP::Route4::from_string(addr),
|
||||
addr4, dev_, iface_to_ignore) < 0)
|
||||
{
|
||||
OPENVPN_THROW(linux_gw_netlink_error,
|
||||
"error retrieving default IPv4 GW");
|
||||
@@ -98,11 +108,19 @@ namespace openvpn {
|
||||
std::string dev_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Provides IPv4/6 gateway which is used to reach given address
|
||||
*
|
||||
* @param iface_to_ignore this allows to exclude certain interface
|
||||
* from discovered gateways. Used when we want to exclude VPN interface
|
||||
* when there is active VPN connection with redirected default gateway
|
||||
* @param addr address which we want to reach
|
||||
*/
|
||||
struct LinuxGW46Netlink
|
||||
{
|
||||
LinuxGW46Netlink()
|
||||
: v4(false),
|
||||
v6(true)
|
||||
LinuxGW46Netlink(const std::string& iface_to_ignore, const std::string& addr = "")
|
||||
: v4(addr.empty() ? IPv4::Addr::from_zero().to_string() : addr, iface_to_ignore, false),
|
||||
v6(addr.empty() ? IPv6::Addr::from_zero().to_string() : addr, iface_to_ignore, true)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -63,13 +63,13 @@ namespace openvpn {
|
||||
ret = (long)empty();
|
||||
break;
|
||||
case BIO_C_SET_BUF_MEM_EOF_RETURN:
|
||||
b->num = (int)num;
|
||||
return_eof_on_empty = (num == 0);
|
||||
break;
|
||||
case BIO_CTRL_GET_CLOSE:
|
||||
ret = (long)b->shutdown;
|
||||
ret = (long)(BIO_get_shutdown (b));
|
||||
break;
|
||||
case BIO_CTRL_SET_CLOSE:
|
||||
b->shutdown = (int)num;
|
||||
BIO_set_shutdown (b, (int)num);
|
||||
break;
|
||||
case BIO_CTRL_WPENDING:
|
||||
ret = 0L;
|
||||
@@ -91,7 +91,7 @@ namespace openvpn {
|
||||
ret = mtu = num;
|
||||
break;
|
||||
case BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT:
|
||||
std::memcpy(&next_timeout, ptr, sizeof(struct timeval));
|
||||
std::memcpy(&next_timeout, ptr, sizeof(struct timeval));
|
||||
break;
|
||||
default:
|
||||
//OPENVPN_LOG("*** MemQ-dgram unimplemented ctrl method=" << cmd);
|
||||
@@ -104,23 +104,23 @@ namespace openvpn {
|
||||
private:
|
||||
long mtu;
|
||||
long query_mtu_return;
|
||||
bool return_eof_on_empty;
|
||||
struct timeval next_timeout;
|
||||
};
|
||||
|
||||
namespace bio_memq_internal {
|
||||
enum {
|
||||
BIO_TYPE_MEMQ = (94|BIO_TYPE_SOURCE_SINK) // make sure type 94 doesn't collide with anything in bio.h
|
||||
};
|
||||
static int memq_method_type=0;
|
||||
static BIO_METHOD* memq_method = nullptr;
|
||||
|
||||
inline int memq_new (BIO *b)
|
||||
{
|
||||
MemQ *bmq = new MemQ();
|
||||
MemQ *bmq = new(std::nothrow) MemQ();
|
||||
if (!bmq)
|
||||
return 0;
|
||||
b->shutdown = 1;
|
||||
b->init = 1;
|
||||
b->num = -1;
|
||||
b->ptr = (void *)bmq;
|
||||
BIO_set_shutdown(b, 1);
|
||||
BIO_set_init(b, 1);
|
||||
b->return_eof_on_empty = false;
|
||||
BIO_set_data(b, (void *)bmq);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -128,13 +128,13 @@ namespace openvpn {
|
||||
{
|
||||
if (b == nullptr)
|
||||
return (0);
|
||||
if (b->shutdown)
|
||||
if (BIO_get_shutdown (b))
|
||||
{
|
||||
if ((b->init) && (b->ptr != nullptr))
|
||||
MemQ *bmq = (MemQ*) (BIO_get_data (b));
|
||||
if (BIO_get_init (b) && (bmq != nullptr))
|
||||
{
|
||||
MemQ *bmq = (MemQ*)b->ptr;
|
||||
delete bmq;
|
||||
b->ptr = nullptr;
|
||||
BIO_set_data (b, nullptr);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
@@ -142,7 +142,7 @@ namespace openvpn {
|
||||
|
||||
inline int memq_write (BIO *b, const char *in, int len)
|
||||
{
|
||||
MemQ *bmq = (MemQ*)b->ptr;
|
||||
MemQ *bmq = (MemQ*) (BIO_get_data (b));
|
||||
if (in)
|
||||
{
|
||||
BIO_clear_retry_flags (b);
|
||||
@@ -166,7 +166,7 @@ namespace openvpn {
|
||||
|
||||
inline int memq_read (BIO *b, char *out, int size)
|
||||
{
|
||||
MemQ *bmq = (MemQ*)b->ptr;
|
||||
MemQ *bmq = (MemQ*) (BIO_get_data (b));
|
||||
int ret = -1;
|
||||
BIO_clear_retry_flags (b);
|
||||
if (!bmq->empty())
|
||||
@@ -191,7 +191,7 @@ namespace openvpn {
|
||||
|
||||
inline long memq_ctrl (BIO *b, int cmd, long arg1, void *arg2)
|
||||
{
|
||||
MemQ *bmq = (MemQ*)b->ptr;
|
||||
MemQ *bmq = (MemQ*) (BIO_get_data (b));
|
||||
return bmq->ctrl(b, cmd, arg1, arg2);
|
||||
}
|
||||
|
||||
@@ -202,43 +202,60 @@ namespace openvpn {
|
||||
return ret;
|
||||
}
|
||||
|
||||
BIO_METHOD memq_method =
|
||||
{
|
||||
BIO_TYPE_MEMQ,
|
||||
"datagram memory queue",
|
||||
memq_write,
|
||||
memq_read,
|
||||
memq_puts,
|
||||
nullptr, /* memq_gets */
|
||||
memq_ctrl,
|
||||
memq_new,
|
||||
memq_free,
|
||||
nullptr,
|
||||
};
|
||||
inline void create_bio_method ()
|
||||
{
|
||||
if (!memq_method_type)
|
||||
memq_method_type = BIO_get_new_index ();
|
||||
|
||||
memq_method = BIO_meth_new (memq_method_type, "datagram memory queue");
|
||||
BIO_meth_set_write (memq_method, memq_write);
|
||||
BIO_meth_set_read (memq_read);
|
||||
BIO_meth_set_puts (memq_puts);
|
||||
BIO_meth_set_create (memq_new);
|
||||
BIO_meth_set_destroy (memq_destroy);
|
||||
BIO_meth_set_gets (nullptr);
|
||||
BIO_meth_set_ctrl (memq_method, memq_ctrl);
|
||||
}
|
||||
|
||||
inline void free_bio_method()
|
||||
{
|
||||
BIO_meth_free (memq_method);
|
||||
}
|
||||
} // namespace bio_memq_internal
|
||||
|
||||
inline BIO_METHOD *BIO_s_memq(void)
|
||||
{
|
||||
return (&bio_memq_internal::memq_method);
|
||||
// TODO: call free in some cleanup
|
||||
bio_memq_internal::create_bio_method ();
|
||||
return bio_memq_internal::memq_method;
|
||||
}
|
||||
|
||||
inline MemQ *memq_from_bio(BIO *b)
|
||||
{
|
||||
if (b->method->type == bio_memq_internal::BIO_TYPE_MEMQ)
|
||||
return (MemQ *)b->ptr;
|
||||
if (BIO_method_type (b) == bio_memq_internal::memq_method_type)
|
||||
return (MemQ *)(BIO_get_data (b));
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline const MemQ *const_memq_from_bio(const BIO *b)
|
||||
{
|
||||
if (b->method->type == bio_memq_internal::BIO_TYPE_MEMQ)
|
||||
return (const MemQ *)b->ptr;
|
||||
if (BIO_method_type (b) == bio_memq_internal::memq_method_type)
|
||||
return (const MemQ *)(BIO_get_data (const_cast<BIO*>(b)));
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MemQ()
|
||||
{
|
||||
mtu = 0;
|
||||
query_mtu_return = 0;
|
||||
std::memset(&next_timeout, 0, sizeof(next_timeout));
|
||||
|
||||
bio_memq_internal::create_bio_method ();
|
||||
}
|
||||
|
||||
~MemQ() { bio_memq_internal::free_bio_method(); }
|
||||
} // namespace bmq_dgram
|
||||
} // namespace openvpn
|
||||
|
||||
|
||||
@@ -22,8 +22,7 @@
|
||||
// This code implements an OpenSSL BIO object for streams based on the
|
||||
// MemQ buffer queue object.
|
||||
|
||||
#ifndef OPENVPN_OPENSSL_BIO_BIO_MEMQ_STREAM_H
|
||||
#define OPENVPN_OPENSSL_BIO_BIO_MEMQ_STREAM_H
|
||||
#pragma once
|
||||
|
||||
#include <cstring> // for std::strlen and others
|
||||
|
||||
@@ -35,6 +34,8 @@
|
||||
#include <openvpn/frame/frame.hpp>
|
||||
#include <openvpn/frame/memq_stream.hpp>
|
||||
|
||||
#include <openvpn/openssl/compat.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace bmq_stream {
|
||||
|
||||
@@ -55,13 +56,13 @@ namespace openvpn {
|
||||
ret = (long)empty();
|
||||
break;
|
||||
case BIO_C_SET_BUF_MEM_EOF_RETURN:
|
||||
b->num = (int)num;
|
||||
return_eof_on_empty = (num == 0);
|
||||
break;
|
||||
case BIO_CTRL_GET_CLOSE:
|
||||
ret = (long)b->shutdown;
|
||||
ret = BIO_get_shutdown(b);
|
||||
break;
|
||||
case BIO_CTRL_SET_CLOSE:
|
||||
b->shutdown = (int)num;
|
||||
BIO_set_shutdown(b, (int) num);
|
||||
break;
|
||||
case BIO_CTRL_WPENDING:
|
||||
ret = 0L;
|
||||
@@ -80,22 +81,23 @@ namespace openvpn {
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
bool return_eof_on_empty = false;
|
||||
};
|
||||
|
||||
namespace bio_memq_internal {
|
||||
enum {
|
||||
BIO_TYPE_MEMQ = (95|BIO_TYPE_SOURCE_SINK) // make sure type 95 doesn't collide with anything in bio.h
|
||||
};
|
||||
|
||||
static int memq_method_type=0;
|
||||
static BIO_METHOD* memq_method = nullptr;
|
||||
|
||||
inline int memq_new (BIO *b)
|
||||
{
|
||||
MemQ *bmq = new MemQ();
|
||||
MemQ *bmq = new(std::nothrow) MemQ();
|
||||
if (!bmq)
|
||||
return 0;
|
||||
b->shutdown = 1;
|
||||
b->init = 1;
|
||||
b->num = -1;
|
||||
b->ptr = (void *)bmq;
|
||||
BIO_set_shutdown(b, 1);
|
||||
BIO_set_init(b, 1);
|
||||
BIO_set_data(b, (void *)bmq);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -103,13 +105,13 @@ namespace openvpn {
|
||||
{
|
||||
if (b == nullptr)
|
||||
return (0);
|
||||
if (b->shutdown)
|
||||
if (BIO_get_shutdown (b))
|
||||
{
|
||||
if ((b->init) && (b->ptr != nullptr))
|
||||
MemQ *bmq = (MemQ*) (BIO_get_data (b));
|
||||
if (BIO_get_init (b) && (bmq != nullptr))
|
||||
{
|
||||
MemQ *bmq = (MemQ*)b->ptr;
|
||||
delete bmq;
|
||||
b->ptr = nullptr;
|
||||
BIO_set_data (b, nullptr);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
@@ -117,7 +119,7 @@ namespace openvpn {
|
||||
|
||||
inline int memq_write (BIO *b, const char *in, int len)
|
||||
{
|
||||
MemQ *bmq = (MemQ*)b->ptr;
|
||||
MemQ *bmq = (MemQ*)(BIO_get_data(b));
|
||||
if (in)
|
||||
{
|
||||
BIO_clear_retry_flags (b);
|
||||
@@ -141,7 +143,7 @@ namespace openvpn {
|
||||
|
||||
inline int memq_read (BIO *b, char *out, int size)
|
||||
{
|
||||
MemQ *bmq = (MemQ*)b->ptr;
|
||||
MemQ *bmq = (MemQ*)(BIO_get_data(b));
|
||||
int ret = -1;
|
||||
BIO_clear_retry_flags (b);
|
||||
if (!bmq->empty())
|
||||
@@ -151,22 +153,21 @@ namespace openvpn {
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
BIOerr(BIO_F_MEM_READ, BIO_R_INVALID_ARGUMENT);
|
||||
BIOerr(memq_method_type, BIO_R_INVALID_ARGUMENT);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = b->num;
|
||||
if (ret != 0)
|
||||
BIO_set_retry_read (b);
|
||||
if (!bmq->return_eof_on_empty)
|
||||
BIO_set_retry_read (b);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline long memq_ctrl (BIO *b, int cmd, long arg1, void *arg2)
|
||||
{
|
||||
MemQ *bmq = (MemQ*)b->ptr;
|
||||
MemQ *bmq = (MemQ*)(BIO_get_data(b));
|
||||
return bmq->ctrl(b, cmd, arg1, arg2);
|
||||
}
|
||||
|
||||
@@ -177,44 +178,50 @@ namespace openvpn {
|
||||
return ret;
|
||||
}
|
||||
|
||||
BIO_METHOD memq_method =
|
||||
{
|
||||
BIO_TYPE_MEMQ,
|
||||
"stream memory queue",
|
||||
memq_write,
|
||||
memq_read,
|
||||
memq_puts,
|
||||
nullptr, /* memq_gets */
|
||||
memq_ctrl,
|
||||
memq_new,
|
||||
memq_free,
|
||||
nullptr,
|
||||
};
|
||||
inline void init_static ()
|
||||
{
|
||||
memq_method_type = BIO_get_new_index ();
|
||||
memq_method = BIO_meth_new (memq_method_type, "stream memory queue");
|
||||
BIO_meth_set_write (memq_method, memq_write);
|
||||
BIO_meth_set_read (memq_method, memq_read);
|
||||
BIO_meth_set_puts (memq_method, memq_puts);
|
||||
BIO_meth_set_create (memq_method, memq_new);
|
||||
BIO_meth_set_destroy (memq_method, memq_free);
|
||||
BIO_meth_set_gets (memq_method, nullptr);
|
||||
BIO_meth_set_ctrl (memq_method, memq_ctrl);
|
||||
}
|
||||
|
||||
inline void free_bio_method()
|
||||
{
|
||||
BIO_meth_free (memq_method);
|
||||
memq_method = nullptr;
|
||||
}
|
||||
} // namespace bio_memq_internal
|
||||
|
||||
inline void init_static()
|
||||
{
|
||||
bio_memq_internal::init_static();
|
||||
}
|
||||
|
||||
inline BIO_METHOD *BIO_s_memq(void)
|
||||
{
|
||||
return (&bio_memq_internal::memq_method);
|
||||
return (bio_memq_internal::memq_method);
|
||||
}
|
||||
|
||||
inline MemQ *memq_from_bio(BIO *b)
|
||||
{
|
||||
if (b->method->type == bio_memq_internal::BIO_TYPE_MEMQ)
|
||||
return (MemQ *)b->ptr;
|
||||
if (BIO_method_type(b) == bio_memq_internal::memq_method_type)
|
||||
return (MemQ *)(BIO_get_data (b));
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline const MemQ *const_memq_from_bio(const BIO *b)
|
||||
{
|
||||
if (b->method->type == bio_memq_internal::BIO_TYPE_MEMQ)
|
||||
return (const MemQ *)b->ptr;
|
||||
if (BIO_method_type(b) == bio_memq_internal::memq_method_type)
|
||||
return (const MemQ *)(BIO_get_data (const_cast<BIO*>(b)));
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace bmq_dgram
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_OPENSSL_BIO_BIO_MEMQ_STREAM_H
|
||||
|
||||
@@ -0,0 +1,336 @@
|
||||
// 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
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/crypto.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/dsa.h>
|
||||
#include <openssl/hmac.h>
|
||||
|
||||
// make sure type 94 doesn't collide with anything in bio.h
|
||||
// Start with the same number as before
|
||||
|
||||
static int lastindex = 94;
|
||||
inline int BIO_get_new_index(void)
|
||||
{
|
||||
int newval = lastindex | BIO_TYPE_SOURCE_SINK;
|
||||
lastindex++;
|
||||
return newval;
|
||||
}
|
||||
|
||||
inline BIO_METHOD *BIO_meth_new(int type, const char *name)
|
||||
{
|
||||
BIO_METHOD *biom = new BIO_METHOD();
|
||||
|
||||
if ((biom->name = OPENSSL_strdup(name)) == nullptr)
|
||||
{
|
||||
delete biom;
|
||||
BIOerr(BIO_F_BIO_NEW, ERR_R_MALLOC_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
biom->type = type;
|
||||
return biom;
|
||||
}
|
||||
|
||||
inline void BIO_meth_free(BIO_METHOD *biom)
|
||||
{
|
||||
if (biom != nullptr)
|
||||
{
|
||||
OPENSSL_free((void *)biom->name);
|
||||
delete biom;
|
||||
}
|
||||
}
|
||||
|
||||
inline RSA_METHOD *RSA_meth_new(const char *name, int flags)
|
||||
{
|
||||
RSA_METHOD *meth = new RSA_METHOD();
|
||||
|
||||
meth->flags = flags;
|
||||
meth->name = name;
|
||||
|
||||
return meth;
|
||||
}
|
||||
|
||||
inline void RSA_meth_free(RSA_METHOD *meth)
|
||||
{
|
||||
delete meth;
|
||||
}
|
||||
|
||||
inline HMAC_CTX *HMAC_CTX_new()
|
||||
{
|
||||
HMAC_CTX *ctx = new HMAC_CTX();
|
||||
HMAC_CTX_init(ctx);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
inline void HMAC_CTX_free(HMAC_CTX *ctx)
|
||||
{
|
||||
HMAC_CTX_cleanup(ctx);
|
||||
delete ctx;
|
||||
}
|
||||
|
||||
inline EVP_MD_CTX *EVP_MD_CTX_new()
|
||||
{
|
||||
return new EVP_MD_CTX();
|
||||
}
|
||||
|
||||
void EVP_MD_CTX_free(EVP_MD_CTX *ctx)
|
||||
{
|
||||
delete ctx;
|
||||
}
|
||||
|
||||
inline void BIO_set_shutdown(BIO *a, int shut)
|
||||
{
|
||||
a->shutdown = shut;
|
||||
}
|
||||
|
||||
inline int BIO_get_shutdown(BIO *a)
|
||||
{
|
||||
return a->shutdown;
|
||||
}
|
||||
|
||||
inline void BIO_set_data(BIO *a, void *ptr)
|
||||
{
|
||||
a->ptr = ptr;
|
||||
}
|
||||
|
||||
inline void *BIO_get_data(BIO *a)
|
||||
{
|
||||
return a->ptr;
|
||||
}
|
||||
|
||||
inline void BIO_set_init(BIO *a, int init)
|
||||
{
|
||||
a->init = init;
|
||||
}
|
||||
|
||||
inline int BIO_get_init(BIO *a)
|
||||
{
|
||||
return a->init;
|
||||
}
|
||||
|
||||
inline int BIO_meth_set_write(BIO_METHOD *biom,
|
||||
int (*bwrite)(BIO *, const char *, int))
|
||||
{
|
||||
biom->bwrite = bwrite;
|
||||
return 1;
|
||||
}
|
||||
|
||||
inline int BIO_meth_set_read(BIO_METHOD *biom,
|
||||
int (*bread)(BIO *, char *, int))
|
||||
{
|
||||
biom->bread = bread;
|
||||
return 1;
|
||||
}
|
||||
|
||||
inline int BIO_meth_set_puts(BIO_METHOD *biom,
|
||||
int (*bputs)(BIO *, const char *))
|
||||
{
|
||||
biom->bputs = bputs;
|
||||
return 1;
|
||||
}
|
||||
|
||||
inline int BIO_meth_set_gets(BIO_METHOD *biom,
|
||||
int (*bgets)(BIO *, char *, int))
|
||||
{
|
||||
biom->bgets = bgets;
|
||||
return 1;
|
||||
}
|
||||
|
||||
inline int BIO_meth_set_ctrl(BIO_METHOD *biom,
|
||||
long (*ctrl)(BIO *, int, long, void *))
|
||||
{
|
||||
biom->ctrl = ctrl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
inline int BIO_meth_set_create(BIO_METHOD *biom, int (*create)(BIO *))
|
||||
{
|
||||
biom->create = create;
|
||||
return 1;
|
||||
}
|
||||
|
||||
inline int BIO_meth_set_destroy(BIO_METHOD *biom, int (*destroy)(BIO *))
|
||||
{
|
||||
biom->destroy = destroy;
|
||||
return 1;
|
||||
}
|
||||
|
||||
inline RSA *EVP_PKEY_get0_RSA(EVP_PKEY *pkey)
|
||||
{
|
||||
return pkey->pkey.rsa;
|
||||
}
|
||||
|
||||
inline int RSA_meth_set_pub_enc(RSA_METHOD *meth,
|
||||
int (*pub_enc)(int flen, const unsigned char *from,
|
||||
unsigned char *to, RSA *rsa,
|
||||
int padding))
|
||||
{
|
||||
meth->rsa_pub_enc = pub_enc;
|
||||
return 1;
|
||||
}
|
||||
|
||||
inline int RSA_meth_set_pub_dec(RSA_METHOD *meth,
|
||||
int (*pub_dec)(int flen, const unsigned char *from,
|
||||
unsigned char *to, RSA *rsa,
|
||||
int padding))
|
||||
{
|
||||
meth->rsa_pub_dec = pub_dec;
|
||||
return 1;
|
||||
}
|
||||
|
||||
inline int RSA_meth_set_priv_enc(RSA_METHOD *meth,
|
||||
int (*priv_enc)(int flen, const unsigned char *from,
|
||||
unsigned char *to, RSA *rsa,
|
||||
int padding))
|
||||
{
|
||||
meth->rsa_priv_enc = priv_enc;
|
||||
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))
|
||||
{
|
||||
meth->rsa_priv_dec = priv_dec;
|
||||
return 1;
|
||||
}
|
||||
|
||||
inline int RSA_meth_set_init(RSA_METHOD *meth, int (*init)(RSA *rsa))
|
||||
{
|
||||
meth->init = init;
|
||||
return 1;
|
||||
}
|
||||
|
||||
inline int RSA_meth_set_finish(RSA_METHOD *meth, int (*finish)(RSA *rsa))
|
||||
{
|
||||
meth->finish = finish;
|
||||
return 1;
|
||||
}
|
||||
|
||||
inline int RSA_meth_set0_app_data(RSA_METHOD *meth, void *app_data)
|
||||
{
|
||||
meth->app_data = (char *) app_data;
|
||||
return 1;
|
||||
}
|
||||
|
||||
inline void *RSA_meth_get0_app_data(const RSA_METHOD *meth)
|
||||
{
|
||||
return (void *) meth->app_data;
|
||||
}
|
||||
|
||||
inline DSA *EVP_PKEY_get0_DSA(EVP_PKEY *pkey)
|
||||
{
|
||||
return pkey->pkey.dsa;
|
||||
}
|
||||
|
||||
inline void DSA_get0_pqg(const DSA *d, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g)
|
||||
{
|
||||
if (p != nullptr)
|
||||
*p = d->p;
|
||||
|
||||
if (q != nullptr)
|
||||
*q = d->q;
|
||||
|
||||
if (g != nullptr)
|
||||
*g = d->g;
|
||||
}
|
||||
|
||||
inline void RSA_set_flags(RSA *r, int flags)
|
||||
{
|
||||
r->flags |= flags;
|
||||
}
|
||||
|
||||
inline int RSA_set0_key(RSA *rsa, BIGNUM *n, BIGNUM *e, BIGNUM *d)
|
||||
{
|
||||
if ((rsa->n == nullptr && n == nullptr)
|
||||
|| (rsa->e == nullptr && e == nullptr))
|
||||
return 0;
|
||||
|
||||
if (n != nullptr)
|
||||
{
|
||||
BN_free(rsa->n);
|
||||
rsa->n = n;
|
||||
}
|
||||
|
||||
if (e != nullptr)
|
||||
{
|
||||
BN_free(rsa->e);
|
||||
rsa->e = e;
|
||||
}
|
||||
|
||||
if (d != nullptr)
|
||||
{
|
||||
BN_free(rsa->d);
|
||||
rsa->d = d;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
inline void RSA_get0_key(const RSA *rsa, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d)
|
||||
{
|
||||
if (n != nullptr)
|
||||
*n = rsa->n;
|
||||
|
||||
if (e != nullptr)
|
||||
*e = rsa->e;
|
||||
|
||||
if (d != nullptr)
|
||||
*d = rsa->d;
|
||||
}
|
||||
|
||||
/* 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
|
||||
#endif
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10101000L
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/dsa.h>
|
||||
|
||||
inline const BIGNUM *RSA_get0_n(const RSA *r)
|
||||
{
|
||||
const BIGNUM *n;
|
||||
RSA_get0_key(r, &n, nullptr, nullptr);
|
||||
return n;
|
||||
}
|
||||
|
||||
inline const BIGNUM *RSA_get0_e(const RSA *r)
|
||||
{
|
||||
const BIGNUM *e;
|
||||
RSA_get0_key(r, nullptr, &e, nullptr);
|
||||
return e;
|
||||
}
|
||||
|
||||
inline const BIGNUM *DSA_get0_p(const DSA *d)
|
||||
{
|
||||
const BIGNUM *p;
|
||||
DSA_get0_pqg(d, &p, nullptr, nullptr);
|
||||
return p;
|
||||
}
|
||||
#endif
|
||||
@@ -74,8 +74,9 @@ namespace openvpn {
|
||||
if (!(mode == ENCRYPT || mode == DECRYPT))
|
||||
throw openssl_cipher_mode_error();
|
||||
erase();
|
||||
EVP_CIPHER_CTX_init (&ctx);
|
||||
if (!EVP_CipherInit_ex (&ctx, cipher_type(alg), nullptr, key, nullptr, mode))
|
||||
ctx = EVP_CIPHER_CTX_new ();
|
||||
EVP_CIPHER_CTX_init (ctx);
|
||||
if (!EVP_CipherInit_ex (ctx, cipher_type(alg), nullptr, key, nullptr, mode))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
throw openssl_cipher_error("EVP_CipherInit_ex (init)");
|
||||
@@ -86,7 +87,7 @@ namespace openvpn {
|
||||
void reset(const unsigned char *iv)
|
||||
{
|
||||
check_initialized();
|
||||
if (!EVP_CipherInit_ex (&ctx, nullptr, nullptr, nullptr, iv, -1))
|
||||
if (!EVP_CipherInit_ex (ctx, nullptr, nullptr, nullptr, iv, -1))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
throw openssl_cipher_error("EVP_CipherInit_ex (reset)");
|
||||
@@ -99,7 +100,7 @@ namespace openvpn {
|
||||
{
|
||||
check_initialized();
|
||||
int outlen;
|
||||
if (EVP_CipherUpdate (&ctx, out, &outlen, in, int(in_size)))
|
||||
if (EVP_CipherUpdate (ctx, out, &outlen, in, int(in_size)))
|
||||
{
|
||||
out_acc += outlen;
|
||||
return true;
|
||||
@@ -115,7 +116,7 @@ namespace openvpn {
|
||||
{
|
||||
check_initialized();
|
||||
int outlen;
|
||||
if (EVP_CipherFinal_ex (&ctx, out, &outlen))
|
||||
if (EVP_CipherFinal_ex (ctx, out, &outlen))
|
||||
{
|
||||
out_acc += outlen;
|
||||
return true;
|
||||
@@ -132,20 +133,20 @@ namespace openvpn {
|
||||
size_t iv_length() const
|
||||
{
|
||||
check_initialized();
|
||||
return EVP_CIPHER_CTX_iv_length (&ctx);
|
||||
return EVP_CIPHER_CTX_iv_length (ctx);
|
||||
}
|
||||
|
||||
size_t block_size() const
|
||||
{
|
||||
check_initialized();
|
||||
return EVP_CIPHER_CTX_block_size (&ctx);
|
||||
return EVP_CIPHER_CTX_block_size (ctx);
|
||||
}
|
||||
|
||||
// return cipher mode (such as CIPH_CBC_MODE, etc.)
|
||||
int cipher_mode() const
|
||||
{
|
||||
check_initialized();
|
||||
return EVP_CIPHER_CTX_mode (&ctx);
|
||||
return EVP_CIPHER_CTX_mode (ctx);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -176,7 +177,8 @@ namespace openvpn {
|
||||
{
|
||||
if (initialized)
|
||||
{
|
||||
EVP_CIPHER_CTX_cleanup(&ctx);
|
||||
EVP_CIPHER_CTX_cleanup(ctx);
|
||||
EVP_CIPHER_CTX_free (ctx);
|
||||
initialized = false;
|
||||
}
|
||||
}
|
||||
@@ -190,7 +192,7 @@ namespace openvpn {
|
||||
}
|
||||
|
||||
bool initialized;
|
||||
EVP_CIPHER_CTX ctx;
|
||||
EVP_CIPHER_CTX* ctx;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,18 +77,19 @@ namespace openvpn {
|
||||
const EVP_CIPHER *ciph = cipher_type(alg, ckeysz);
|
||||
if (ckeysz > keysize)
|
||||
throw openssl_gcm_error("insufficient key material");
|
||||
EVP_CIPHER_CTX_init(&ctx);
|
||||
ctx = EVP_CIPHER_CTX_new();
|
||||
EVP_CIPHER_CTX_init(ctx);
|
||||
switch (mode)
|
||||
{
|
||||
case ENCRYPT:
|
||||
if (!EVP_EncryptInit_ex(&ctx, ciph, nullptr, key, nullptr))
|
||||
if (!EVP_EncryptInit_ex(ctx, ciph, nullptr, key, nullptr))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
throw openssl_gcm_error("EVP_EncryptInit_ex (init)");
|
||||
}
|
||||
break;
|
||||
case DECRYPT:
|
||||
if (!EVP_DecryptInit_ex(&ctx, ciph, nullptr, key, nullptr))
|
||||
if (!EVP_DecryptInit_ex(ctx, ciph, nullptr, key, nullptr))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
throw openssl_gcm_error("EVP_DecryptInit_ex (init)");
|
||||
@@ -97,7 +98,7 @@ namespace openvpn {
|
||||
default:
|
||||
throw openssl_gcm_error("bad mode");
|
||||
}
|
||||
if (EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_SET_IVLEN, IV_LEN, nullptr) != 1)
|
||||
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, IV_LEN, nullptr) != 1)
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
throw openssl_gcm_error("EVP_CIPHER_CTX_ctrl set IV len");
|
||||
@@ -117,23 +118,23 @@ namespace openvpn {
|
||||
int ciphertext_len;
|
||||
|
||||
check_initialized();
|
||||
if (!EVP_EncryptInit_ex(&ctx, nullptr, nullptr, nullptr, iv))
|
||||
if (!EVP_EncryptInit_ex(ctx, nullptr, nullptr, nullptr, iv))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
throw openssl_gcm_error("EVP_EncryptInit_ex (reset)");
|
||||
}
|
||||
if (!EVP_EncryptUpdate(&ctx, nullptr, &len, ad, int(ad_len)))
|
||||
if (!EVP_EncryptUpdate(ctx, nullptr, &len, ad, int(ad_len)))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
throw openssl_gcm_error("EVP_EncryptUpdate AD");
|
||||
}
|
||||
if (!EVP_EncryptUpdate(&ctx, output, &len, input, int(length)))
|
||||
if (!EVP_EncryptUpdate(ctx, output, &len, input, int(length)))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
throw openssl_gcm_error("EVP_EncryptUpdate data");
|
||||
}
|
||||
ciphertext_len = len;
|
||||
if (!EVP_EncryptFinal_ex(&ctx, output+len, &len))
|
||||
if (!EVP_EncryptFinal_ex(ctx, output+len, &len))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
throw openssl_gcm_error("EVP_EncryptFinal_ex");
|
||||
@@ -143,7 +144,7 @@ namespace openvpn {
|
||||
{
|
||||
throw openssl_gcm_error("encrypt size inconsistency");
|
||||
}
|
||||
if (!EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_GET_TAG, AUTH_TAG_LEN, tag))
|
||||
if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, AUTH_TAG_LEN, tag))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
throw openssl_gcm_error("EVP_CIPHER_CTX_ctrl get tag");
|
||||
@@ -162,28 +163,28 @@ namespace openvpn {
|
||||
int plaintext_len;
|
||||
|
||||
check_initialized();
|
||||
if (!EVP_DecryptInit_ex(&ctx, nullptr, nullptr, nullptr, iv))
|
||||
if (!EVP_DecryptInit_ex(ctx, nullptr, nullptr, nullptr, iv))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
throw openssl_gcm_error("EVP_DecryptInit_ex (reset)");
|
||||
}
|
||||
if (!EVP_DecryptUpdate(&ctx, nullptr, &len, ad, int(ad_len)))
|
||||
if (!EVP_DecryptUpdate(ctx, nullptr, &len, ad, int(ad_len)))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
throw openssl_gcm_error("EVP_DecryptUpdate AD");
|
||||
}
|
||||
if (!EVP_DecryptUpdate(&ctx, output, &len, input, int(length)))
|
||||
if (!EVP_DecryptUpdate(ctx, output, &len, input, int(length)))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
throw openssl_gcm_error("EVP_DecryptUpdate data");
|
||||
}
|
||||
plaintext_len = len;
|
||||
if (!EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_SET_TAG, AUTH_TAG_LEN, tag))
|
||||
if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, AUTH_TAG_LEN, tag))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
throw openssl_gcm_error("EVP_CIPHER_CTX_ctrl set tag");
|
||||
}
|
||||
if (!EVP_DecryptFinal_ex(&ctx, output+len, &len))
|
||||
if (!EVP_DecryptFinal_ex(ctx, output+len, &len))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
return false;
|
||||
@@ -222,7 +223,7 @@ namespace openvpn {
|
||||
{
|
||||
if (initialized)
|
||||
{
|
||||
EVP_CIPHER_CTX_cleanup(&ctx);
|
||||
EVP_CIPHER_CTX_cleanup(ctx);
|
||||
initialized = false;
|
||||
}
|
||||
}
|
||||
@@ -234,7 +235,7 @@ namespace openvpn {
|
||||
}
|
||||
|
||||
bool initialized;
|
||||
EVP_CIPHER_CTX ctx;
|
||||
EVP_CIPHER_CTX *ctx;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,8 @@
|
||||
#include <openvpn/crypto/cryptoalgs.hpp>
|
||||
#include <openvpn/openssl/util/error.hpp>
|
||||
|
||||
#include <openvpn/openssl/compat.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace OpenSSLCrypto {
|
||||
class HMACContext;
|
||||
@@ -74,7 +76,8 @@ namespace openvpn {
|
||||
void init(const CryptoAlgs::Type alg)
|
||||
{
|
||||
erase();
|
||||
if (!EVP_DigestInit(&ctx, digest_type(alg)))
|
||||
ctx=EVP_MD_CTX_new ();
|
||||
if (!EVP_DigestInit(ctx, digest_type(alg)))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
throw openssl_digest_error("EVP_DigestInit");
|
||||
@@ -85,7 +88,7 @@ namespace openvpn {
|
||||
void update(const unsigned char *in, const size_t size)
|
||||
{
|
||||
check_initialized();
|
||||
if (!EVP_DigestUpdate(&ctx, in, int(size)))
|
||||
if (!EVP_DigestUpdate(ctx, in, int(size)))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
throw openssl_digest_error("EVP_DigestUpdate");
|
||||
@@ -96,7 +99,7 @@ namespace openvpn {
|
||||
{
|
||||
check_initialized();
|
||||
unsigned int outlen;
|
||||
if (!EVP_DigestFinal(&ctx, out, &outlen))
|
||||
if (!EVP_DigestFinal(ctx, out, &outlen))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
throw openssl_digest_error("EVP_DigestFinal");
|
||||
@@ -107,7 +110,7 @@ namespace openvpn {
|
||||
size_t size() const
|
||||
{
|
||||
check_initialized();
|
||||
return EVP_MD_CTX_size(&ctx);
|
||||
return EVP_MD_CTX_size(ctx);
|
||||
}
|
||||
|
||||
bool is_initialized() const { return initialized; }
|
||||
@@ -140,7 +143,10 @@ namespace openvpn {
|
||||
{
|
||||
if (initialized)
|
||||
{
|
||||
EVP_MD_CTX_cleanup(&ctx);
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
EVP_MD_CTX_cleanup(ctx);
|
||||
#endif
|
||||
EVP_MD_CTX_free(ctx);
|
||||
initialized = false;
|
||||
}
|
||||
}
|
||||
@@ -154,7 +160,7 @@ namespace openvpn {
|
||||
}
|
||||
|
||||
bool initialized;
|
||||
EVP_MD_CTX ctx;
|
||||
EVP_MD_CTX *ctx;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/openssl/crypto/digest.hpp>
|
||||
|
||||
#include <openvpn/openssl/compat.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace OpenSSLCrypto {
|
||||
class HMACContext
|
||||
@@ -62,60 +64,45 @@ namespace openvpn {
|
||||
void init(const CryptoAlgs::Type digest, const unsigned char *key, const size_t key_size)
|
||||
{
|
||||
erase();
|
||||
HMAC_CTX_init (&ctx);
|
||||
#if SSLEAY_VERSION_NUMBER >= 0x10000000L
|
||||
if (!HMAC_Init_ex (&ctx, key, int(key_size), DigestContext::digest_type(digest), nullptr))
|
||||
ctx = HMAC_CTX_new ();
|
||||
if (!HMAC_Init_ex (ctx, key, int(key_size), DigestContext::digest_type(digest), nullptr))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
throw openssl_hmac_error("HMAC_Init_ex (init)");
|
||||
}
|
||||
#else
|
||||
HMAC_Init_ex (&ctx, key, int(key_size), DigestContext::digest_type(digest), nullptr);
|
||||
#endif
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
check_initialized();
|
||||
#if SSLEAY_VERSION_NUMBER >= 0x10000000L
|
||||
if (!HMAC_Init_ex (&ctx, nullptr, 0, nullptr, nullptr))
|
||||
if (!HMAC_Init_ex (ctx, nullptr, 0, nullptr, nullptr))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
throw openssl_hmac_error("HMAC_Init_ex (reset)");
|
||||
}
|
||||
#else
|
||||
HMAC_Init_ex (&ctx, nullptr, 0, nullptr, nullptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
void update(const unsigned char *in, const size_t size)
|
||||
{
|
||||
check_initialized();
|
||||
#if SSLEAY_VERSION_NUMBER >= 0x10000000L
|
||||
if (!HMAC_Update(&ctx, in, int(size)))
|
||||
|
||||
if (!HMAC_Update(ctx, in, int(size)))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
throw openssl_hmac_error("HMAC_Update");
|
||||
}
|
||||
#else
|
||||
HMAC_Update(&ctx, in, int(size));
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t final(unsigned char *out)
|
||||
{
|
||||
check_initialized();
|
||||
unsigned int outlen;
|
||||
#if SSLEAY_VERSION_NUMBER >= 0x10000000L
|
||||
if (!HMAC_Final(&ctx, out, &outlen))
|
||||
if (!HMAC_Final(ctx, out, &outlen))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
throw openssl_hmac_error("HMAC_Final");
|
||||
}
|
||||
#else
|
||||
HMAC_Final(&ctx, out, &outlen);
|
||||
#endif
|
||||
return outlen;
|
||||
}
|
||||
|
||||
@@ -132,14 +119,16 @@ namespace openvpn {
|
||||
{
|
||||
if (initialized)
|
||||
{
|
||||
HMAC_CTX_cleanup(&ctx);
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
#endif
|
||||
HMAC_CTX_free(ctx);
|
||||
initialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
size_t size_() const
|
||||
{
|
||||
return HMAC_size(&ctx);
|
||||
return HMAC_size(ctx);
|
||||
}
|
||||
|
||||
void check_initialized() const
|
||||
@@ -151,7 +140,7 @@ namespace openvpn {
|
||||
}
|
||||
|
||||
bool initialized;
|
||||
HMAC_CTX ctx;
|
||||
HMAC_CTX* ctx;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
// 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 <string>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <tuple>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/common/msfind.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
// Client-side session cache.
|
||||
// (We don't cache server-side sessions because we use TLS
|
||||
// session resumption tickets which are stateless on the server).
|
||||
class OpenSSLSessionCache : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<OpenSSLSessionCache> Ptr;
|
||||
|
||||
OPENVPN_EXCEPTION(openssl_sess_cache_error);
|
||||
|
||||
// Wrapper for OpenSSL SSL_SESSION pointers that manages reference counts.
|
||||
class Session
|
||||
{
|
||||
public:
|
||||
Session(::SSL_SESSION* sess) // caller must pre-increment refcount on sess
|
||||
: sess_(sess)
|
||||
{
|
||||
}
|
||||
|
||||
Session(Session&& other) noexcept
|
||||
{
|
||||
sess_ = other.sess_;
|
||||
other.sess_ = nullptr;
|
||||
}
|
||||
|
||||
Session& operator=(Session&& other) noexcept
|
||||
{
|
||||
if (sess_)
|
||||
::SSL_SESSION_free(sess_);
|
||||
sess_ = other.sess_;
|
||||
other.sess_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
::SSL_SESSION* openssl_session() const
|
||||
{
|
||||
return sess_;
|
||||
}
|
||||
|
||||
bool operator<(const Session& rhs) const // used when Session is a std::set key
|
||||
{
|
||||
return sess_ < rhs.sess_;
|
||||
}
|
||||
|
||||
explicit operator bool() const
|
||||
{
|
||||
return sess_ != nullptr;
|
||||
}
|
||||
|
||||
~Session()
|
||||
{
|
||||
if (sess_)
|
||||
::SSL_SESSION_free(sess_);
|
||||
}
|
||||
|
||||
private:
|
||||
// These methods are deleted because we have no way to increment
|
||||
// an SSL_SESSION refcount until OpenSSL 1.1.
|
||||
Session(const Session&) = delete;
|
||||
Session& operator=(const Session&) = delete;
|
||||
|
||||
::SSL_SESSION* sess_;
|
||||
};
|
||||
|
||||
class Key
|
||||
{
|
||||
public:
|
||||
typedef std::unique_ptr<Key> UPtr;
|
||||
|
||||
Key(const std::string& key_arg,
|
||||
OpenSSLSessionCache::Ptr cache_arg)
|
||||
: key(key_arg),
|
||||
cache(std::move(cache_arg))
|
||||
{
|
||||
//OPENVPN_LOG("OpenSSLSessionCache::Key CONSTRUCT key=" << key);
|
||||
}
|
||||
|
||||
void commit(::SSL_SESSION* sess)
|
||||
{
|
||||
if (!sess)
|
||||
return;
|
||||
auto mi = MSF::find(cache->map, key);
|
||||
if (mi)
|
||||
{
|
||||
/* auto ins = */ mi->second.emplace(sess);
|
||||
//OPENVPN_LOG("OpenSSLSessionCache::Key::commit ADD=" << ins.second << " key=" << key);
|
||||
}
|
||||
else
|
||||
{
|
||||
//OPENVPN_LOG("OpenSSLSessionCache::Key::commit CREATE key=" << key);
|
||||
auto ins = cache->map.emplace(std::piecewise_construct,
|
||||
std::forward_as_tuple(key),
|
||||
std::forward_as_tuple());
|
||||
ins.first->second.emplace(sess);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const std::string key;
|
||||
OpenSSLSessionCache::Ptr cache;
|
||||
};
|
||||
|
||||
// Remove a session from the map after calling func() on it.
|
||||
// This would be a lot cleaner if we had C++17 std::set::extract().
|
||||
template <typename FUNC>
|
||||
void extract(const std::string& key, FUNC func)
|
||||
{
|
||||
auto mi = MSF::find(map, key);
|
||||
if (mi)
|
||||
{
|
||||
//OPENVPN_LOG("OpenSSLSessionCache::Key::lookup EXISTS key=" << key);
|
||||
SessionSet& ss = mi->second;
|
||||
if (ss.empty())
|
||||
throw openssl_sess_cache_error("internal error: SessionSet is empty");
|
||||
auto ssi = ss.begin();
|
||||
try {
|
||||
func(ssi->openssl_session());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
remove_session(mi, ss, ssi);
|
||||
throw;
|
||||
}
|
||||
remove_session(mi, ss, ssi);
|
||||
}
|
||||
else
|
||||
{
|
||||
//OPENVPN_LOG("OpenSSLSessionCache::Key::lookup NOT_FOUND key=" << key);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
struct SessionSet : public std::set<Session>
|
||||
{
|
||||
};
|
||||
|
||||
typedef std::map<std::string, SessionSet> Map;
|
||||
|
||||
void remove_session(Map::iterator mi, SessionSet& ss, SessionSet::iterator ssi)
|
||||
{
|
||||
ss.erase(ssi);
|
||||
if (ss.empty())
|
||||
map.erase(mi);
|
||||
}
|
||||
|
||||
Map map;
|
||||
};
|
||||
|
||||
}
|
||||
+429
-132
@@ -32,6 +32,8 @@
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/x509v3.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/rand.h>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
@@ -60,6 +62,7 @@
|
||||
#include <openvpn/openssl/pki/dh.hpp>
|
||||
#include <openvpn/openssl/pki/x509store.hpp>
|
||||
#include <openvpn/openssl/bio/bio_memq_stream.hpp>
|
||||
#include <openvpn/openssl/ssl/sess_cache.hpp>
|
||||
|
||||
// An SSL Context is essentially a configuration that can be used
|
||||
// to generate an arbitrary number of actual SSL connections objects.
|
||||
@@ -89,16 +92,6 @@ namespace openvpn {
|
||||
public:
|
||||
typedef RCPtr<Config> Ptr;
|
||||
|
||||
Config() : external_pki(nullptr),
|
||||
ssl_debug_level(0),
|
||||
flags(0),
|
||||
ns_cert_type(NSCert::NONE),
|
||||
tls_version_min(TLSVersion::UNDEF),
|
||||
tls_cert_profile(TLSCertProfile::UNDEF),
|
||||
local_cert_enabled(true),
|
||||
force_aes_cbc_ciphersuites(false),
|
||||
enable_renegotiation(false) {}
|
||||
|
||||
virtual SSLFactoryAPI::Ptr new_factory()
|
||||
{
|
||||
return SSLFactoryAPI::Ptr(new OpenSSLContext(this));
|
||||
@@ -120,6 +113,18 @@ namespace openvpn {
|
||||
external_pki = external_pki_arg;
|
||||
}
|
||||
|
||||
// server side
|
||||
virtual void set_session_ticket_handler(TLSSessionTicketBase* session_ticket_handler_arg)
|
||||
{
|
||||
session_ticket_handler = session_ticket_handler_arg;
|
||||
}
|
||||
|
||||
// client side
|
||||
virtual void set_client_session_tickets(const bool v)
|
||||
{
|
||||
client_session_tickets = v;
|
||||
}
|
||||
|
||||
virtual void set_private_key_password(const std::string& pwd)
|
||||
{
|
||||
pkey.set_private_key_password(pwd);
|
||||
@@ -259,11 +264,6 @@ namespace openvpn {
|
||||
local_cert_enabled = v;
|
||||
}
|
||||
|
||||
virtual void set_enable_renegotiation(const bool v)
|
||||
{
|
||||
enable_renegotiation = v;
|
||||
}
|
||||
|
||||
virtual void set_force_aes_cbc_ciphersuites(const bool v)
|
||||
{
|
||||
force_aes_cbc_ciphersuites = v;
|
||||
@@ -407,20 +407,21 @@ namespace openvpn {
|
||||
OpenSSLPKI::X509List extra_certs; // from OpenVPN "extra-certs" option
|
||||
OpenSSLPKI::PKey pkey; // private key
|
||||
OpenSSLPKI::DH dh; // diffie-hellman parameters (only needed in server mode)
|
||||
ExternalPKIBase* external_pki;
|
||||
ExternalPKIBase* external_pki = nullptr;
|
||||
TLSSessionTicketBase* session_ticket_handler = nullptr; // server side only
|
||||
Frame::Ptr frame;
|
||||
int ssl_debug_level;
|
||||
unsigned int flags; // defined in sslconsts.hpp
|
||||
NSCert::Type ns_cert_type;
|
||||
int ssl_debug_level = 0;
|
||||
unsigned int flags = 0; // defined in sslconsts.hpp
|
||||
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
|
||||
std::string tls_remote;
|
||||
TLSVersion::Type tls_version_min; // minimum TLS version that we will negotiate
|
||||
TLSCertProfile::Type tls_cert_profile;
|
||||
TLSVersion::Type tls_version_min{TLSVersion::UNDEF}; // minimum TLS version that we will negotiate
|
||||
TLSCertProfile::Type tls_cert_profile{TLSCertProfile::UNDEF};
|
||||
X509Track::ConfigSet x509_track_config;
|
||||
bool local_cert_enabled;
|
||||
bool force_aes_cbc_ciphersuites;
|
||||
bool enable_renegotiation;
|
||||
bool local_cert_enabled = true;
|
||||
bool force_aes_cbc_ciphersuites = false;
|
||||
bool client_session_tickets = false;
|
||||
};
|
||||
|
||||
// Represents an actual SSL session.
|
||||
@@ -445,7 +446,10 @@ namespace openvpn {
|
||||
if (status == -1 && BIO_should_retry(ssl_bio))
|
||||
return SSLConst::SHOULD_RETRY;
|
||||
else
|
||||
OPENVPN_THROW(OpenSSLException, "OpenSSLContext::SSL::write_cleartext: BIO_write failed, size=" << size << " status=" << status);
|
||||
{
|
||||
mark_no_cache();
|
||||
OPENVPN_THROW(OpenSSLException, "OpenSSLContext::SSL::write_cleartext: BIO_write failed, size=" << size << " status=" << status);
|
||||
}
|
||||
}
|
||||
else
|
||||
return status;
|
||||
@@ -461,7 +465,10 @@ namespace openvpn {
|
||||
if (status == -1 && BIO_should_retry(ssl_bio))
|
||||
return SSLConst::SHOULD_RETRY;
|
||||
else
|
||||
OPENVPN_THROW(OpenSSLException, "OpenSSLContext::SSL::read_cleartext: BIO_read failed, cap=" << capacity << " status=" << status);
|
||||
{
|
||||
mark_no_cache();
|
||||
OPENVPN_THROW(OpenSSLException, "OpenSSLContext::SSL::read_cleartext: BIO_read failed, cap=" << capacity << " status=" << status);
|
||||
}
|
||||
}
|
||||
else
|
||||
return status;
|
||||
@@ -508,11 +515,31 @@ namespace openvpn {
|
||||
return ssl_handshake_details(ssl);
|
||||
}
|
||||
|
||||
virtual const AuthCert::Ptr& auth_cert() const
|
||||
// Return true if we did a full SSL handshake/negotiation.
|
||||
// Return false for cached, reused, or persisted sessions.
|
||||
// Also returns false if previously called on this session.
|
||||
virtual bool did_full_handshake() override
|
||||
{
|
||||
if (called_did_full_handshake)
|
||||
return false;
|
||||
called_did_full_handshake = true;
|
||||
return !SSL_session_reused(ssl);
|
||||
}
|
||||
|
||||
virtual const AuthCert::Ptr& auth_cert()
|
||||
{
|
||||
// Reused sessions don't call the cert verify callbacks,
|
||||
// so we must use an alternative method to build authcert.
|
||||
if (authcert && authcert->is_uninitialized())
|
||||
rebuild_authcert();
|
||||
return authcert;
|
||||
}
|
||||
|
||||
virtual void mark_no_cache()
|
||||
{
|
||||
sess_cache_key.reset();
|
||||
}
|
||||
|
||||
~SSL()
|
||||
{
|
||||
ssl_erase();
|
||||
@@ -520,26 +547,39 @@ namespace openvpn {
|
||||
|
||||
static void init_static()
|
||||
{
|
||||
SSL_library_init();
|
||||
bmq_stream::init_static();
|
||||
|
||||
mydata_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);
|
||||
|
||||
// We actually override some of the OpenSSL SSLv23 methods here,
|
||||
// in particular the ssl_pending method. We want ssl_pending
|
||||
// to return 0 until the SSL negotiation establishes the
|
||||
// actual method. The default OpenSSL SSLv23 ssl_pending method
|
||||
// (ssl_undefined_const_function) triggers an OpenSSL error condition
|
||||
// which is not what we want.
|
||||
/*
|
||||
* We actually override some of the OpenSSL SSLv23 methods here,
|
||||
* in particular the ssl_pending method. We want ssl_pending
|
||||
* to return 0 until the SSL negotiation establishes the
|
||||
* actual method. The default OpenSSL SSLv23 ssl_pending method
|
||||
* (ssl_undefined_const_function) triggers an OpenSSL error condition
|
||||
* when calling SSL_pending early which is not what we want.
|
||||
*
|
||||
* This depends on SSL23 being a generic method and OpenSSL later
|
||||
* switching to a spefic TLS method (TLS10method etc..) with
|
||||
* ssl23_get_client_method that has the proper ssl3_pending pending method.
|
||||
*
|
||||
* OpenSSL 1.1.x does not allow hacks like this anymore. So overriding is not
|
||||
* possible. Fortunately OpenSSL 1.1 also always defines ssl_pending method to
|
||||
* be ssl3_pending, so this hack is no longer needed.
|
||||
*/
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
ssl23_method_client_ = *SSLv23_client_method();
|
||||
ssl23_method_client_.ssl_pending = ssl_pending_override;
|
||||
|
||||
ssl23_method_server_ = *SSLv23_server_method();
|
||||
ssl23_method_server_.ssl_pending = ssl_pending_override;
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
SSL(const OpenSSLContext& ctx, const char *hostname)
|
||||
SSL(const OpenSSLContext& ctx, const std::string* hostname, const std::string* cache_key)
|
||||
{
|
||||
ssl_clear();
|
||||
try {
|
||||
@@ -556,7 +596,7 @@ namespace openvpn {
|
||||
{
|
||||
X509_VERIFY_PARAM *param = SSL_get0_param(ssl);
|
||||
X509_VERIFY_PARAM_set_hostflags(param, 0);
|
||||
X509_VERIFY_PARAM_set1_host(param, hostname, 0);
|
||||
X509_VERIFY_PARAM_set1_host(param, hostname->c_str(), 0);
|
||||
}
|
||||
|
||||
// init BIOs
|
||||
@@ -576,6 +616,17 @@ namespace openvpn {
|
||||
}
|
||||
else if (ctx.config->mode.is_client())
|
||||
{
|
||||
if (cache_key && ctx.sess_cache)
|
||||
{
|
||||
// see if a cached session already exists for our cache_key
|
||||
ctx.sess_cache->extract(*cache_key, [this](SSL_SESSION* sess) {
|
||||
if (!SSL_set_session(ssl, sess))
|
||||
throw OpenSSLException("SSL_set_session failed");
|
||||
});
|
||||
|
||||
// cache the session before its end-of-life if no errors occur
|
||||
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)
|
||||
@@ -591,7 +642,10 @@ namespace openvpn {
|
||||
|
||||
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);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@@ -600,6 +654,29 @@ namespace openvpn {
|
||||
}
|
||||
}
|
||||
|
||||
void rebuild_authcert()
|
||||
{
|
||||
authcert.reset(new AuthCert());
|
||||
::X509 *cert = SSL_get_peer_certificate(ssl);
|
||||
if (cert)
|
||||
{
|
||||
// save the issuer cert fingerprint
|
||||
static_assert(sizeof(AuthCert::issuer_fp) == SHA_DIGEST_LENGTH, "size inconsistency");
|
||||
|
||||
unsigned int md_len = sizeof(AuthCert::issuer_fp);
|
||||
X509_digest (cert, EVP_sha1 (), authcert->issuer_fp, &md_len);
|
||||
|
||||
// save the Common Name
|
||||
authcert->cn = x509_get_field(cert, NID_commonName);
|
||||
|
||||
// save the leaf cert serial number
|
||||
const ASN1_INTEGER *ai = X509_get_serialNumber(cert);
|
||||
authcert->sn = ai ? ASN1_INTEGER_get(ai) : -1;
|
||||
|
||||
X509_free (cert);
|
||||
}
|
||||
}
|
||||
|
||||
// Indicate no data available for our custom SSLv23 method
|
||||
static int ssl_pending_override(const ::SSL *)
|
||||
{
|
||||
@@ -607,12 +684,15 @@ namespace openvpn {
|
||||
}
|
||||
|
||||
// Print a one line summary of SSL/TLS session handshake.
|
||||
static std::string ssl_handshake_details (const ::SSL *c_ssl)
|
||||
static std::string ssl_handshake_details (::SSL *c_ssl)
|
||||
{
|
||||
std::ostringstream os;
|
||||
|
||||
os << SSL_get_version (c_ssl);
|
||||
|
||||
const SSL_CIPHER *ciph = SSL_get_current_cipher (c_ssl);
|
||||
os << SSL_get_version (c_ssl) << ", cipher " << SSL_CIPHER_get_version (ciph) << ' ' << SSL_CIPHER_get_name (ciph);
|
||||
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)
|
||||
@@ -620,16 +700,18 @@ namespace openvpn {
|
||||
EVP_PKEY *pkey = X509_get_pubkey (cert);
|
||||
if (pkey != nullptr)
|
||||
{
|
||||
if (pkey->type == EVP_PKEY_RSA && pkey->pkey.rsa != nullptr && pkey->pkey.rsa->n != nullptr)
|
||||
os << ", " << BN_num_bits (pkey->pkey.rsa->n) << " bit RSA";
|
||||
if (EVP_PKEY_id (pkey) == EVP_PKEY_RSA && EVP_PKEY_get0_RSA (pkey) != nullptr && RSA_get0_n(EVP_PKEY_get0_RSA (pkey)) != nullptr)
|
||||
os << ", " << BN_num_bits (RSA_get0_n(EVP_PKEY_get0_RSA (pkey))) << " bit RSA";
|
||||
#ifndef OPENSSL_NO_DSA
|
||||
else if (pkey->type == EVP_PKEY_DSA && pkey->pkey.dsa != nullptr && pkey->pkey.dsa->p != nullptr)
|
||||
os << ", " << BN_num_bits (pkey->pkey.dsa->p) << " bit DSA";
|
||||
else if (EVP_PKEY_id (pkey) == EVP_PKEY_DSA && EVP_PKEY_get0_DSA (pkey) != nullptr && DSA_get0_p(EVP_PKEY_get0_DSA (pkey))!= nullptr)
|
||||
os << ", " << BN_num_bits (DSA_get0_p(EVP_PKEY_get0_DSA (pkey))) << " bit DSA";
|
||||
#endif
|
||||
EVP_PKEY_free (pkey);
|
||||
}
|
||||
X509_free (cert);
|
||||
}
|
||||
if (SSL_session_reused(c_ssl))
|
||||
os << " [REUSED]";
|
||||
return os.str();
|
||||
}
|
||||
|
||||
@@ -641,6 +723,8 @@ namespace openvpn {
|
||||
ct_in = nullptr;
|
||||
ct_out = nullptr;
|
||||
overflow = false;
|
||||
called_did_full_handshake = false;
|
||||
sess_cache_key.reset();
|
||||
}
|
||||
|
||||
void ssl_erase()
|
||||
@@ -655,7 +739,14 @@ namespace openvpn {
|
||||
if (ssl_bio)
|
||||
BIO_free_all(ssl_bio);
|
||||
if (ssl)
|
||||
SSL_free(ssl);
|
||||
{
|
||||
if (sess_cache_key)
|
||||
{
|
||||
SSL_set_shutdown(ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN);
|
||||
sess_cache_key->commit(SSL_get1_session(ssl));
|
||||
}
|
||||
SSL_free(ssl);
|
||||
}
|
||||
openssl_clear_error_stack();
|
||||
ssl_clear();
|
||||
}
|
||||
@@ -669,35 +760,51 @@ namespace openvpn {
|
||||
return bio;
|
||||
}
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
/*
|
||||
* Return modified OpenSSL SSLv23 methods,
|
||||
* as configured in init_static().
|
||||
*/
|
||||
|
||||
static const SSL_METHOD* ssl23_method_client()
|
||||
static const SSL_METHOD* tls_method_client()
|
||||
{
|
||||
return &ssl23_method_client_;
|
||||
}
|
||||
|
||||
static const SSL_METHOD* ssl23_method_server()
|
||||
static const SSL_METHOD* tls_method_server()
|
||||
{
|
||||
return &ssl23_method_server_;
|
||||
}
|
||||
#else
|
||||
static const SSL_METHOD* tls_method_client ()
|
||||
{
|
||||
return TLS_client_method ();
|
||||
}
|
||||
|
||||
static const SSL_METHOD* tls_method_server ()
|
||||
{
|
||||
return TLS_server_method ();
|
||||
}
|
||||
#endif
|
||||
::SSL *ssl; // OpenSSL SSL object
|
||||
BIO *ssl_bio; // read/write cleartext from here
|
||||
BIO *ct_in; // write ciphertext to here
|
||||
BIO *ct_out; // read ciphertext from here
|
||||
AuthCert::Ptr authcert;
|
||||
OpenSSLSessionCache::Key::UPtr sess_cache_key; // client-side only
|
||||
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 context_data_index;
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
// Modified SSLv23 methods
|
||||
static SSL_METHOD ssl23_method_client_;
|
||||
static SSL_METHOD ssl23_method_server_;
|
||||
#endif
|
||||
};
|
||||
|
||||
private:
|
||||
@@ -707,22 +814,20 @@ namespace openvpn {
|
||||
: external_pki(external_pki_arg), n_errors(0)
|
||||
{
|
||||
RSA *rsa = nullptr;
|
||||
RSA_METHOD *rsa_meth = nullptr;
|
||||
RSA *pub_rsa = nullptr;
|
||||
RSA *pub_rsa = nullptr;
|
||||
RSA_METHOD *rsa_meth = nullptr;
|
||||
const char *errtext = "";
|
||||
|
||||
/* allocate custom RSA method object */
|
||||
rsa_meth = new RSA_METHOD;
|
||||
std::memset(rsa_meth, 0, sizeof(RSA_METHOD));
|
||||
rsa_meth->name = "OpenSSLContext::ExternalPKIImpl private key RSA Method";
|
||||
rsa_meth->rsa_pub_enc = rsa_pub_enc;
|
||||
rsa_meth->rsa_pub_dec = rsa_pub_dec;
|
||||
rsa_meth->rsa_priv_enc = rsa_priv_enc;
|
||||
rsa_meth->rsa_priv_dec = rsa_priv_dec;
|
||||
rsa_meth->init = nullptr;
|
||||
rsa_meth->finish = rsa_finish;
|
||||
rsa_meth->flags = RSA_METHOD_FLAG_NO_CHECK;
|
||||
rsa_meth->app_data = (char *)this;
|
||||
rsa_meth = RSA_meth_new ("OpenSSLContext::ExternalPKIImpl private key RSA Method", RSA_METHOD_FLAG_NO_CHECK);
|
||||
|
||||
RSA_meth_set_pub_enc (rsa_meth, rsa_pub_enc);
|
||||
RSA_meth_set_pub_dec (rsa_meth, rsa_pub_dec);
|
||||
RSA_meth_set_priv_enc (rsa_meth, rsa_priv_enc);
|
||||
RSA_meth_set_priv_dec (rsa_meth, rsa_priv_dec);
|
||||
RSA_meth_set_init (rsa_meth, nullptr);
|
||||
RSA_meth_set_finish (rsa_meth, rsa_finish);
|
||||
RSA_meth_set0_app_data (rsa_meth, this);
|
||||
|
||||
/* allocate RSA object */
|
||||
rsa = RSA_new();
|
||||
@@ -734,16 +839,26 @@ namespace openvpn {
|
||||
}
|
||||
|
||||
/* get the public key */
|
||||
if (cert->cert_info->key->pkey == nullptr) /* nullptr before SSL_CTX_use_certificate() is called */
|
||||
if (X509_get0_pubkey(cert) == nullptr) /* nullptr before SSL_CTX_use_certificate() is called */
|
||||
{
|
||||
errtext = "pkey is NULL";
|
||||
goto err;
|
||||
}
|
||||
pub_rsa = cert->cert_info->key->pkey->pkey.rsa;
|
||||
|
||||
if (EVP_PKEY_id (X509_get0_pubkey(cert)) != EVP_PKEY_RSA )
|
||||
{
|
||||
errtext = "pkey is not RSA";
|
||||
goto err;
|
||||
}
|
||||
pub_rsa = EVP_PKEY_get0_RSA (X509_get0_pubkey(cert));
|
||||
|
||||
/* initialize RSA object */
|
||||
rsa->n = BN_dup(pub_rsa->n);
|
||||
rsa->flags |= RSA_FLAG_EXT_PKEY;
|
||||
rsa = RSA_new ();
|
||||
|
||||
/* only set e and n as d (private key) is outside our control */
|
||||
RSA_set0_key(rsa, BN_dup(RSA_get0_n(pub_rsa)), BN_dup(RSA_get0_e(pub_rsa)), nullptr);
|
||||
RSA_set_flags (rsa, RSA_FLAG_EXT_PKEY);
|
||||
|
||||
if (!RSA_set_method(rsa, rsa_meth))
|
||||
{
|
||||
errtext = "RSA_set_method";
|
||||
@@ -766,7 +881,7 @@ namespace openvpn {
|
||||
else
|
||||
{
|
||||
if (rsa_meth)
|
||||
free(rsa_meth);
|
||||
RSA_meth_free (rsa_meth);
|
||||
}
|
||||
OPENVPN_THROW(OpenSSLException, "OpenSSLContext::ExternalPKIImpl: " << errtext);
|
||||
}
|
||||
@@ -779,21 +894,29 @@ namespace openvpn {
|
||||
/* called at RSA_free */
|
||||
static int rsa_finish(RSA *rsa)
|
||||
{
|
||||
free ((void*)rsa->meth);
|
||||
rsa->meth = nullptr;
|
||||
RSA_meth_free (const_cast<RSA_METHOD*>(RSA_get_method (rsa)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* sign arbitrary data */
|
||||
static int rsa_priv_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding)
|
||||
{
|
||||
ExternalPKIImpl* self = (ExternalPKIImpl*)rsa->meth->app_data;
|
||||
ExternalPKIImpl* self = (ExternalPKIImpl*)(RSA_meth_get0_app_data (RSA_get_method(rsa)));
|
||||
|
||||
try {
|
||||
if (padding != RSA_PKCS1_PADDING)
|
||||
if (padding != RSA_PKCS1_PADDING && padding != RSA_NO_PADDING)
|
||||
{
|
||||
RSAerr (RSA_F_RSA_EAY_PRIVATE_ENCRYPT, RSA_R_UNKNOWN_PADDING_TYPE);
|
||||
throw ssl_external_pki("OpenSSL: bad padding size");
|
||||
RSAerr (RSA_F_RSA_OSSL_PRIVATE_ENCRYPT, RSA_R_UNKNOWN_PADDING_TYPE);
|
||||
throw ssl_external_pki("OpenSSL: bad padding type");
|
||||
}
|
||||
std::string padding_algo;
|
||||
if (padding == RSA_PKCS1_PADDING)
|
||||
{
|
||||
padding_algo = "RSA_PKCS1_PADDING";
|
||||
}
|
||||
else if (padding == RSA_NO_PADDING)
|
||||
{
|
||||
padding_algo = "RSA_NO_PADDING";
|
||||
}
|
||||
|
||||
/* convert 'from' to base64 */
|
||||
@@ -802,7 +925,7 @@ namespace openvpn {
|
||||
|
||||
/* get signature */
|
||||
std::string sig_b64;
|
||||
const bool status = self->external_pki->sign(from_b64, sig_b64);
|
||||
const bool status = self->external_pki->sign(from_b64, sig_b64, padding_algo);
|
||||
if (!status)
|
||||
throw ssl_external_pki("OpenSSL: could not obtain signature");
|
||||
|
||||
@@ -828,7 +951,7 @@ namespace openvpn {
|
||||
|
||||
static void not_implemented(RSA *rsa)
|
||||
{
|
||||
ExternalPKIImpl* self = (ExternalPKIImpl*)rsa->meth->app_data;
|
||||
ExternalPKIImpl* self = (ExternalPKIImpl*)(RSA_meth_get0_app_data (RSA_get_method (rsa)));
|
||||
++self->n_errors;
|
||||
}
|
||||
|
||||
@@ -862,17 +985,14 @@ namespace openvpn {
|
||||
/////// start of main class implementation
|
||||
|
||||
OpenSSLContext(Config* config_arg)
|
||||
: config(config_arg),
|
||||
ctx(nullptr),
|
||||
epki(nullptr)
|
||||
: config(config_arg)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create new SSL_CTX for server or client mode
|
||||
const bool ssl23 = (!config->force_aes_cbc_ciphersuites || (config->tls_version_min > TLSVersion::UNDEF));
|
||||
if (config->mode.is_server())
|
||||
{
|
||||
ctx = SSL_CTX_new(ssl23 ? SSL::ssl23_method_server() : TLSv1_server_method());
|
||||
ctx = SSL_CTX_new(SSL::tls_method_server());
|
||||
if (ctx == nullptr)
|
||||
throw OpenSSLException("OpenSSLContext: SSL_CTX_new failed for server method");
|
||||
|
||||
@@ -881,48 +1001,84 @@ namespace openvpn {
|
||||
OPENVPN_THROW(ssl_context_error, "OpenSSLContext: DH not defined");
|
||||
if (!SSL_CTX_set_tmp_dh(ctx, config->dh.obj()))
|
||||
throw OpenSSLException("OpenSSLContext: SSL_CTX_set_tmp_dh failed");
|
||||
if (config->enable_renegotiation)
|
||||
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER);
|
||||
if (config->flags & SSLConst::SERVER_TO_SERVER)
|
||||
SSL_CTX_set_purpose(ctx, X509_PURPOSE_SSL_SERVER);
|
||||
}
|
||||
else if (config->mode.is_client())
|
||||
{
|
||||
ctx = SSL_CTX_new(ssl23 ? SSL::ssl23_method_client() : TLSv1_client_method());
|
||||
ctx = SSL_CTX_new(SSL::tls_method_client());
|
||||
if (ctx == nullptr)
|
||||
throw OpenSSLException("OpenSSLContext: SSL_CTX_new failed for client method");
|
||||
if (config->enable_renegotiation)
|
||||
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_CLIENT); // note: SSL_set_session must be called as well
|
||||
}
|
||||
else
|
||||
OPENVPN_THROW(ssl_context_error, "OpenSSLContext: unknown config->mode");
|
||||
|
||||
// Set SSL options
|
||||
if (!config->enable_renegotiation)
|
||||
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
|
||||
if (!(config->flags & SSLConst::NO_VERIFY_PEER))
|
||||
{
|
||||
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
|
||||
int vf = SSL_VERIFY_PEER;
|
||||
if (!(config->flags & SSLConst::PEER_CERT_OPTIONAL))
|
||||
vf |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
|
||||
SSL_CTX_set_verify(ctx, vf,
|
||||
config->mode.is_client() ? verify_callback_client : verify_callback_server);
|
||||
SSL_CTX_set_verify_depth(ctx, 16);
|
||||
}
|
||||
|
||||
long sslopt = SSL_OP_SINGLE_DH_USE | SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_COMPRESSION;
|
||||
if (!config->enable_renegotiation)
|
||||
sslopt |= SSL_OP_NO_TICKET;
|
||||
if (ssl23)
|
||||
|
||||
/* Disable SSLv2 and SSLv3, might be a noop but does not hurt */
|
||||
sslopt |= SSL_OP_NO_SSLv2;
|
||||
sslopt |= SSL_OP_NO_SSLv3;
|
||||
|
||||
if (config->mode.is_server())
|
||||
{
|
||||
sslopt |= SSL_OP_NO_SSLv2;
|
||||
sslopt |= SSL_OP_NO_SSLv3;
|
||||
if (config->tls_version_min > TLSVersion::V1_0)
|
||||
sslopt |= SSL_OP_NO_TLSv1;
|
||||
# ifdef SSL_OP_NO_TLSv1_1
|
||||
if (config->tls_version_min > TLSVersion::V1_1)
|
||||
sslopt |= SSL_OP_NO_TLSv1_1;
|
||||
# endif
|
||||
# ifdef SSL_OP_NO_TLSv1_2
|
||||
if (config->tls_version_min > TLSVersion::V1_2)
|
||||
sslopt |= SSL_OP_NO_TLSv1_2;
|
||||
# endif
|
||||
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
|
||||
if (config->session_ticket_handler)
|
||||
{
|
||||
const std::string sess_id_context = config->session_ticket_handler->session_id_context();
|
||||
if (!SSL_CTX_set_session_id_context(ctx, (unsigned char *)sess_id_context.c_str(), sess_id_context.length()))
|
||||
throw OpenSSLException("OpenSSLContext: SSL_CTX_set_session_id_context failed");
|
||||
|
||||
if (!SSL_CTX_set_tlsext_ticket_key_cb(ctx, tls_ticket_key_callback))
|
||||
throw OpenSSLException("OpenSSLContext: SSL_CTX_set_tlsext_ticket_key_cb failed");
|
||||
}
|
||||
else
|
||||
sslopt |= SSL_OP_NO_TICKET;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (config->client_session_tickets)
|
||||
{
|
||||
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_CLIENT);
|
||||
sess_cache.reset(new OpenSSLSessionCache);
|
||||
}
|
||||
else
|
||||
{
|
||||
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
|
||||
sslopt |= SSL_OP_NO_TICKET;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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
|
||||
}
|
||||
}
|
||||
SSL_CTX_set_options(ctx, sslopt);
|
||||
|
||||
@@ -933,10 +1089,27 @@ namespace openvpn {
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!SSL_CTX_set_cipher_list(ctx, "DEFAULT:!EXP:!PSK:!SRP:!LOW:!RC4:!kRSA:!MD5:!SSLv2"))
|
||||
OPENVPN_THROW(ssl_context_error, "OpenSSLContext: SSL_CTX_set_cipher_list failed");
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||
SSL_CTX_set_ecdh_auto(ctx, 1);
|
||||
if (!SSL_CTX_set_cipher_list(ctx,
|
||||
/* default list as a basis */
|
||||
"DEFAULT"
|
||||
/* Disable export ciphers, low and medium */
|
||||
":!EXP:!LOW:!MEDIUM"
|
||||
/* Disable static (EC)DH keys (no forward secrecy) */
|
||||
":!kDH:!kECDH"
|
||||
/* Disable DSA private keys */
|
||||
":!DSS"
|
||||
/* Disable RC4 cipher */
|
||||
":!RC4"
|
||||
/* Disable MD5 */
|
||||
":!MD5"
|
||||
/* Disable unsupported TLS modes */
|
||||
":!PSK:!SRP:!kRSA"
|
||||
/* Disable SSLv2 cipher suites*/
|
||||
":!SSLv2"
|
||||
))
|
||||
OPENVPN_THROW(ssl_context_error, "OpenSSLContext: SSL_CTX_set_cipher_list failed");
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10002000L && OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
SSL_CTX_set_ecdh_auto(ctx, 1); // this method becomes a no-op in OpenSSL 1.1
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1019,9 +1192,6 @@ namespace openvpn {
|
||||
else if (!(config->flags & SSLConst::NO_VERIFY_PEER))
|
||||
OPENVPN_THROW(ssl_context_error, "OpenSSLContext: CA not defined");
|
||||
|
||||
// keep a reference to this in ctx, for use by verify callback
|
||||
ctx->app_verify_arg = this;
|
||||
|
||||
// Show handshake debugging info
|
||||
if (config->ssl_debug_level)
|
||||
SSL_CTX_set_info_callback (ctx, info_callback);
|
||||
@@ -1037,13 +1207,13 @@ namespace openvpn {
|
||||
// create a new SSL instance
|
||||
virtual SSLAPI::Ptr ssl()
|
||||
{
|
||||
return SSL::Ptr(new SSL(*this, nullptr));
|
||||
return SSL::Ptr(new SSL(*this, nullptr, nullptr));
|
||||
}
|
||||
|
||||
// like ssl() above but verify hostname against cert CommonName and/or SubjectAltName
|
||||
virtual SSLAPI::Ptr ssl(const std::string& hostname)
|
||||
virtual SSLAPI::Ptr ssl(const std::string* hostname, const std::string* cache_key)
|
||||
{
|
||||
return SSL::Ptr(new SSL(*this, hostname.c_str()));
|
||||
return SSL::Ptr(new SSL(*this, hostname, cache_key));
|
||||
}
|
||||
|
||||
void update_trust(const CertCRLList& cc)
|
||||
@@ -1070,12 +1240,12 @@ namespace openvpn {
|
||||
return config->ns_cert_type != NSCert::NONE;
|
||||
}
|
||||
|
||||
bool verify_ns_cert_type(const ::X509* cert) const
|
||||
bool verify_ns_cert_type(::X509* cert) const
|
||||
{
|
||||
if (config->ns_cert_type == NSCert::SERVER)
|
||||
return (cert->ex_flags & EXFLAG_NSCERT) && (cert->ex_nscert & NS_SSL_SERVER);
|
||||
return X509_check_purpose (cert, X509_PURPOSE_SSL_SERVER, 0);
|
||||
else if (config->ns_cert_type == NSCert::CLIENT)
|
||||
return (cert->ex_flags & EXFLAG_NSCERT) && (cert->ex_nscert & NS_SSL_CLIENT);
|
||||
return X509_check_purpose (cert, X509_PURPOSE_SSL_CLIENT, 0);
|
||||
else
|
||||
return true;
|
||||
}
|
||||
@@ -1285,9 +1455,14 @@ namespace openvpn {
|
||||
x509_get_serial_hex(cert));
|
||||
break;
|
||||
case X509Track::SHA1:
|
||||
xts.emplace_back(X509Track::SHA1,
|
||||
depth,
|
||||
render_hex_sep(cert->sha1_hash, SHA_DIGEST_LENGTH, ':', true));
|
||||
{
|
||||
unsigned char buf[EVP_MAX_MD_SIZE];
|
||||
unsigned int len = EVP_MAX_MD_SIZE;
|
||||
X509_digest (cert, EVP_sha1 (), buf, &len);
|
||||
xts.emplace_back (X509Track::SHA1,
|
||||
depth,
|
||||
render_hex_sep (buf, len, ':', true));
|
||||
}
|
||||
break;
|
||||
case X509Track::CN:
|
||||
x509_track_extract_nid(X509Track::CN, NID_commonName, cert, depth, xts);
|
||||
@@ -1363,13 +1538,16 @@ namespace openvpn {
|
||||
::SSL* ssl = (::SSL*) X509_STORE_CTX_get_ex_data (ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
|
||||
|
||||
// get OpenSSLContext
|
||||
const OpenSSLContext* self = (OpenSSLContext*) ssl->ctx->app_verify_arg;
|
||||
const OpenSSLContext* self = (OpenSSLContext*) SSL_get_ex_data (ssl, SSL::context_data_index);
|
||||
|
||||
// get depth
|
||||
const int depth = X509_STORE_CTX_get_error_depth(ctx);
|
||||
|
||||
// get current certificate
|
||||
X509* current_cert = X509_STORE_CTX_get_current_cert (ctx);
|
||||
|
||||
// log subject
|
||||
const std::string subject = x509_get_subject(ctx->current_cert);
|
||||
const std::string subject = x509_get_subject(current_cert);
|
||||
if (self->config->flags & SSLConst::LOG_VERIFY_STATUS)
|
||||
OPENVPN_LOG_SSL(cert_status_line(preverify_ok, depth, X509_STORE_CTX_get_error(ctx), subject));
|
||||
|
||||
@@ -1377,21 +1555,21 @@ namespace openvpn {
|
||||
if (depth == 0)
|
||||
{
|
||||
// verify ns-cert-type
|
||||
if (self->ns_cert_type_defined() && !self->verify_ns_cert_type(ctx->current_cert))
|
||||
if (self->ns_cert_type_defined() && !self->verify_ns_cert_type(current_cert))
|
||||
{
|
||||
OPENVPN_LOG_SSL("VERIFY FAIL -- bad ns-cert-type in leaf certificate");
|
||||
preverify_ok = false;
|
||||
}
|
||||
|
||||
// verify X509 key usage
|
||||
if (self->x509_cert_ku_defined() && !self->verify_x509_cert_ku(ctx->current_cert))
|
||||
if (self->x509_cert_ku_defined() && !self->verify_x509_cert_ku(current_cert))
|
||||
{
|
||||
OPENVPN_LOG_SSL("VERIFY FAIL -- bad X509 key usage in leaf certificate");
|
||||
preverify_ok = false;
|
||||
}
|
||||
|
||||
// verify X509 extended key usage
|
||||
if (self->x509_cert_eku_defined() && !self->verify_x509_cert_eku(ctx->current_cert))
|
||||
if (self->x509_cert_eku_defined() && !self->verify_x509_cert_eku(current_cert))
|
||||
{
|
||||
OPENVPN_LOG_SSL("VERIFY FAIL -- bad X509 extended key usage in leaf certificate");
|
||||
preverify_ok = false;
|
||||
@@ -1401,7 +1579,7 @@ namespace openvpn {
|
||||
if (!self->config->tls_remote.empty())
|
||||
{
|
||||
const std::string subj = TLSRemote::sanitize_x509_name(subject);
|
||||
const std::string common_name = TLSRemote::sanitize_common_name(x509_get_field(ctx->current_cert, NID_commonName));
|
||||
const std::string common_name = TLSRemote::sanitize_common_name(x509_get_field(current_cert, NID_commonName));
|
||||
TLSRemote::log(self->config->tls_remote, subj, common_name);
|
||||
if (!TLSRemote::test(self->config->tls_remote, subj, common_name))
|
||||
{
|
||||
@@ -1420,7 +1598,7 @@ namespace openvpn {
|
||||
::SSL* ssl = (::SSL*) X509_STORE_CTX_get_ex_data (ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
|
||||
|
||||
// get OpenSSLContext
|
||||
const OpenSSLContext* self = (OpenSSLContext*) ssl->ctx->app_verify_arg;
|
||||
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);
|
||||
@@ -1431,9 +1609,12 @@ namespace openvpn {
|
||||
// get depth
|
||||
const int depth = X509_STORE_CTX_get_error_depth(ctx);
|
||||
|
||||
// get current certificate
|
||||
X509* current_cert = X509_STORE_CTX_get_current_cert (ctx);
|
||||
|
||||
// log subject
|
||||
if (self->config->flags & SSLConst::LOG_VERIFY_STATUS)
|
||||
OPENVPN_LOG_SSL(cert_status_line(preverify_ok, depth, err, x509_get_subject(ctx->current_cert)));
|
||||
OPENVPN_LOG_SSL(cert_status_line(preverify_ok, depth, err, x509_get_subject(current_cert)));
|
||||
|
||||
// record cert error in authcert
|
||||
if (!preverify_ok && self_ssl->authcert)
|
||||
@@ -1445,13 +1626,15 @@ namespace openvpn {
|
||||
if (self_ssl->authcert)
|
||||
{
|
||||
static_assert(sizeof(AuthCert::issuer_fp) == SHA_DIGEST_LENGTH, "size inconsistency");
|
||||
std::memcpy(self_ssl->authcert->issuer_fp, ctx->current_cert->sha1_hash, sizeof(AuthCert::issuer_fp));
|
||||
unsigned int digest_len = sizeof(AuthCert::issuer_fp);
|
||||
if (!X509_digest (current_cert, EVP_sha1 (), self_ssl->authcert->issuer_fp, &digest_len))
|
||||
preverify_ok = false;
|
||||
}
|
||||
}
|
||||
else if (depth == 0) // leaf cert
|
||||
{
|
||||
// verify ns-cert-type
|
||||
if (self->ns_cert_type_defined() && !self->verify_ns_cert_type(ctx->current_cert))
|
||||
if (self->ns_cert_type_defined() && !self->verify_ns_cert_type(current_cert))
|
||||
{
|
||||
OPENVPN_LOG_SSL("VERIFY FAIL -- bad ns-cert-type in leaf certificate");
|
||||
if (self_ssl->authcert)
|
||||
@@ -1460,7 +1643,7 @@ namespace openvpn {
|
||||
}
|
||||
|
||||
// verify X509 key usage
|
||||
if (self->x509_cert_ku_defined() && !self->verify_x509_cert_ku(ctx->current_cert))
|
||||
if (self->x509_cert_ku_defined() && !self->verify_x509_cert_ku(current_cert))
|
||||
{
|
||||
OPENVPN_LOG_SSL("VERIFY FAIL -- bad X509 key usage in leaf certificate");
|
||||
if (self_ssl->authcert)
|
||||
@@ -1469,7 +1652,7 @@ namespace openvpn {
|
||||
}
|
||||
|
||||
// verify X509 extended key usage
|
||||
if (self->x509_cert_eku_defined() && !self->verify_x509_cert_eku(ctx->current_cert))
|
||||
if (self->x509_cert_eku_defined() && !self->verify_x509_cert_eku(current_cert))
|
||||
{
|
||||
OPENVPN_LOG_SSL("VERIFY FAIL -- bad X509 extended key usage in leaf certificate");
|
||||
if (self_ssl->authcert)
|
||||
@@ -1480,17 +1663,17 @@ namespace openvpn {
|
||||
if (self_ssl->authcert)
|
||||
{
|
||||
// save the Common Name
|
||||
self_ssl->authcert->cn = x509_get_field(ctx->current_cert, NID_commonName);
|
||||
self_ssl->authcert->cn = x509_get_field(current_cert, NID_commonName);
|
||||
|
||||
// save the leaf cert serial number
|
||||
const ASN1_INTEGER *ai = X509_get_serialNumber(ctx->current_cert);
|
||||
const ASN1_INTEGER *ai = X509_get_serialNumber(current_cert);
|
||||
self_ssl->authcert->sn = ai ? ASN1_INTEGER_get(ai) : -1;
|
||||
}
|
||||
}
|
||||
|
||||
// x509-track enabled?
|
||||
if (self_ssl->authcert && self_ssl->authcert->x509_track)
|
||||
x509_track_extract_from_cert(ctx->current_cert,
|
||||
x509_track_extract_from_cert(current_cert,
|
||||
depth,
|
||||
self->config->x509_track_config,
|
||||
*self_ssl->authcert->x509_track);
|
||||
@@ -1513,6 +1696,116 @@ namespace openvpn {
|
||||
}
|
||||
}
|
||||
|
||||
static int tls_ticket_key_callback(::SSL *ssl,
|
||||
unsigned char key_name[16],
|
||||
unsigned char iv[EVP_MAX_IV_LENGTH],
|
||||
::EVP_CIPHER_CTX *ctx,
|
||||
::HMAC_CTX *hctx,
|
||||
int enc)
|
||||
{
|
||||
// get OpenSSLContext
|
||||
const OpenSSLContext* self = (OpenSSLContext*) SSL_get_ex_data (ssl, SSL::context_data_index);
|
||||
if (!self)
|
||||
return -1;
|
||||
|
||||
// get user-defined session ticket handler
|
||||
TLSSessionTicketBase* t = self->config->session_ticket_handler;
|
||||
if (!t)
|
||||
return -1;
|
||||
|
||||
if (enc)
|
||||
{
|
||||
// create new ticket
|
||||
TLSSessionTicketBase::Name name;
|
||||
TLSSessionTicketBase::Key key;
|
||||
|
||||
switch (t->create_session_ticket_key(name, key))
|
||||
{
|
||||
case TLSSessionTicketBase::NO_TICKET:
|
||||
case TLSSessionTicketBase::TICKET_EXPIRING: // doesn't really make sense for enc==1?
|
||||
// NOTE: OpenSSL may segfault on a zero return.
|
||||
// This appears to be fixed by:
|
||||
// commit dbdb96617cce2bd4356d57f53ecc327d0e31f2ad
|
||||
// Author: Todd Short <tshort@akamai.com>
|
||||
// Date: Thu May 12 18:16:52 2016 -0400
|
||||
// Fix session ticket and SNI
|
||||
//OPENVPN_LOG("tls_ticket_key_callback: create: no ticket or expiring ticket");
|
||||
#if OPENSSL_VERSION_NUMBER < 0x1000212fL // 1.0.2r
|
||||
if (!randomize_name_key(name, key))
|
||||
return -1;
|
||||
// fallthrough
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
case TLSSessionTicketBase::TICKET_AVAILABLE:
|
||||
if (!RAND_bytes(iv, EVP_MAX_IV_LENGTH))
|
||||
return -1;
|
||||
if (!tls_ticket_init_cipher_hmac(key, iv, ctx, hctx, enc))
|
||||
return -1;
|
||||
static_assert(TLSSessionTicketBase::Name::SIZE == 16, "unexpected name size");
|
||||
std::memcpy(key_name, name.value_, TLSSessionTicketBase::Name::SIZE);
|
||||
//OPENVPN_LOG("tls_ticket_key_callback: created ticket");
|
||||
return 1;
|
||||
default:
|
||||
//OPENVPN_LOG("tls_ticket_key_callback: create: bad ticket");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// lookup existing ticket
|
||||
static_assert(TLSSessionTicketBase::Name::SIZE == 16, "unexpected name size");
|
||||
const TLSSessionTicketBase::Name name(key_name);
|
||||
TLSSessionTicketBase::Key key;
|
||||
|
||||
switch (t->lookup_session_ticket_key(name, key))
|
||||
{
|
||||
case TLSSessionTicketBase::TICKET_AVAILABLE:
|
||||
if (!tls_ticket_init_cipher_hmac(key, iv, ctx, hctx, enc))
|
||||
return -1;
|
||||
//OPENVPN_LOG("tls_ticket_key_callback: found ticket");
|
||||
return 1;
|
||||
case TLSSessionTicketBase::TICKET_EXPIRING:
|
||||
if (!tls_ticket_init_cipher_hmac(key, iv, ctx, hctx, enc))
|
||||
return -1;
|
||||
//OPENVPN_LOG("tls_ticket_key_callback: expiring ticket");
|
||||
return 2;
|
||||
case TLSSessionTicketBase::NO_TICKET:
|
||||
//OPENVPN_LOG("tls_ticket_key_callback: lookup: no ticket");
|
||||
return 0;
|
||||
default:
|
||||
//OPENVPN_LOG("tls_ticket_key_callback: lookup: bad ticket");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool tls_ticket_init_cipher_hmac(const TLSSessionTicketBase::Key& key,
|
||||
unsigned char iv[EVP_MAX_IV_LENGTH],
|
||||
::EVP_CIPHER_CTX *ctx,
|
||||
::HMAC_CTX *hctx,
|
||||
const int enc)
|
||||
{
|
||||
static_assert(TLSSessionTicketBase::Key::CIPHER_KEY_SIZE == 32, "unexpected cipher key size");
|
||||
if (!EVP_CipherInit_ex(ctx, EVP_aes_256_cbc(), nullptr, key.cipher_value_, iv, enc))
|
||||
return false;
|
||||
if (!HMAC_Init_ex(hctx, key.hmac_value_, TLSSessionTicketBase::Key::HMAC_KEY_SIZE, EVP_sha256(), nullptr))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool randomize_name_key(TLSSessionTicketBase::Name& name,
|
||||
TLSSessionTicketBase::Key& key)
|
||||
{
|
||||
if (!RAND_bytes(name.value_, TLSSessionTicketBase::Name::SIZE))
|
||||
return false;
|
||||
if (!RAND_bytes(key.cipher_value_, TLSSessionTicketBase::Key::CIPHER_KEY_SIZE))
|
||||
return false;
|
||||
if (!RAND_bytes(key.hmac_value_, TLSSessionTicketBase::Key::HMAC_KEY_SIZE))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void erase()
|
||||
{
|
||||
if (epki)
|
||||
@@ -1528,14 +1821,18 @@ namespace openvpn {
|
||||
}
|
||||
|
||||
Config::Ptr config;
|
||||
SSL_CTX* ctx;
|
||||
ExternalPKIImpl* epki;
|
||||
SSL_CTX* ctx = nullptr;
|
||||
ExternalPKIImpl* epki = nullptr;
|
||||
OpenSSLSessionCache::Ptr sess_cache; // client-side only
|
||||
};
|
||||
|
||||
int OpenSSLContext::SSL::mydata_index = -1;
|
||||
int OpenSSLContext::SSL::context_data_index = -1;
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
SSL_METHOD OpenSSLContext::SSL::ssl23_method_client_;
|
||||
SSL_METHOD OpenSSLContext::SSL::ssl23_method_server_;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -35,6 +35,8 @@
|
||||
#include <openvpn/random/randapi.hpp>
|
||||
#include <openvpn/openssl/util/error.hpp>
|
||||
|
||||
#include <openvpn/openssl/compat.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
class TokenEncrypt
|
||||
{
|
||||
@@ -63,15 +65,17 @@ namespace openvpn {
|
||||
|
||||
TokenEncrypt(const Key& key, const int mode)
|
||||
{
|
||||
EVP_CIPHER_CTX_init(&ctx);
|
||||
if (!EVP_CipherInit_ex(&ctx, EVP_aes_128_ecb(), nullptr, key.data, nullptr, mode))
|
||||
ctx = EVP_CIPHER_CTX_new ();
|
||||
EVP_CIPHER_CTX_init (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_set_padding(&ctx, 0);
|
||||
EVP_CIPHER_CTX_set_padding(ctx, 0);
|
||||
}
|
||||
|
||||
~TokenEncrypt()
|
||||
{
|
||||
EVP_CIPHER_CTX_cleanup(&ctx);
|
||||
EVP_CIPHER_CTX_cleanup(ctx);
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
}
|
||||
|
||||
// Do the encrypt/decrypt
|
||||
@@ -80,12 +84,12 @@ namespace openvpn {
|
||||
// NOTE: since this algorithm uses the ECB block cipher mode,
|
||||
// it should only be used to encrypt/decrypt a message which
|
||||
// is exactly equal to the AES block size (16 bytes).
|
||||
if (size != EVP_CIPHER_CTX_block_size(&ctx))
|
||||
if (size != EVP_CIPHER_CTX_block_size(ctx))
|
||||
throw Exception("TokenEncrypt: encrypt/decrypt data must be equal to AES block size");
|
||||
int outlen=0;
|
||||
if (!EVP_CipherInit_ex(&ctx, nullptr, nullptr, nullptr, nullptr, -1))
|
||||
if (!EVP_CipherInit_ex(ctx, nullptr, nullptr, nullptr, nullptr, -1))
|
||||
throw OpenSSLException("TokenEncrypt: EVP_CipherInit_ex[2] failed");
|
||||
if (!EVP_CipherUpdate(&ctx, dest, &outlen, src, size))
|
||||
if (!EVP_CipherUpdate(ctx, dest, &outlen, src, size))
|
||||
throw OpenSSLException("TokenEncrypt: EVP_CipherUpdate failed");
|
||||
// NOTE: we skip EVP_CipherFinal_ex because we are running in ECB mode without padding
|
||||
if (outlen != size)
|
||||
@@ -96,7 +100,7 @@ namespace openvpn {
|
||||
TokenEncrypt(const TokenEncrypt&) = delete;
|
||||
TokenEncrypt& operator=(const TokenEncrypt&) = delete;
|
||||
|
||||
EVP_CIPHER_CTX ctx;
|
||||
EVP_CIPHER_CTX* ctx;
|
||||
};
|
||||
|
||||
struct TokenEncryptDecrypt
|
||||
|
||||
@@ -414,6 +414,10 @@ namespace openvpn {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case 's':
|
||||
if (d == "static-key")
|
||||
return true;
|
||||
return false;
|
||||
case 't':
|
||||
if (d == "tls-auth")
|
||||
{
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace openvpn {
|
||||
public:
|
||||
// Sign data (base64) and return signature as sig (base64).
|
||||
// Return true on success or false on error.
|
||||
virtual bool sign(const std::string& data, std::string& sig) = 0;
|
||||
virtual bool sign(const std::string& data, std::string& sig, const std::string& algorithm) = 0;
|
||||
|
||||
virtual ~ExternalPKIBase() {}
|
||||
};
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <openvpn/common/hostport.hpp>
|
||||
#include <openvpn/common/number.hpp>
|
||||
#include <openvpn/common/string.hpp>
|
||||
#include <openvpn/common/format.hpp>
|
||||
#include <openvpn/common/to_string.hpp>
|
||||
#include <openvpn/addr/ip.hpp>
|
||||
#include <openvpn/transport/protocol.hpp>
|
||||
@@ -85,7 +86,10 @@ namespace openvpn {
|
||||
Item port_offset(const unsigned int offset) const
|
||||
{
|
||||
Item ret(*this);
|
||||
ret.port = openvpn::to_string(HostPort::parse_port(ret.port, "offset") + offset);
|
||||
if (ret.proto.is_unix()) // unix socket filenames should contain %s for "port" substitution
|
||||
ret.addr = printfmt(ret.addr, offset);
|
||||
else
|
||||
ret.port = openvpn::to_string(HostPort::parse_port(ret.port, "offset") + offset);
|
||||
ret.n_threads = 0;
|
||||
return ret;
|
||||
}
|
||||
@@ -297,7 +301,7 @@ namespace openvpn {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
List expand_ports(const size_t max_size) const
|
||||
List expand_ports_by_n_threads(const size_t max_size) const
|
||||
{
|
||||
List ret;
|
||||
for (const auto &e : *this)
|
||||
@@ -312,6 +316,14 @@ namespace openvpn {
|
||||
return ret;
|
||||
}
|
||||
|
||||
List expand_ports_by_unit(const unsigned int unit) const
|
||||
{
|
||||
List ret;
|
||||
for (const auto &e : *this)
|
||||
ret.emplace_back(e.port_offset(unit));
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
static bool match(const std::string& directive, const Option& o)
|
||||
{
|
||||
|
||||
@@ -127,6 +127,9 @@ namespace openvpn {
|
||||
// get client bandwidth stats
|
||||
virtual PeerStats stats_poll() = 0;
|
||||
|
||||
// return true if management layer should preserve session ID
|
||||
virtual bool should_preserve_session_id() = 0;
|
||||
|
||||
// get native reference to client instance
|
||||
virtual TunClientInstance::NativeHandle tun_native_handle() = 0;
|
||||
};
|
||||
|
||||
@@ -162,6 +162,11 @@ namespace openvpn {
|
||||
return PeerStats();
|
||||
}
|
||||
|
||||
virtual bool should_preserve_session_id() override
|
||||
{
|
||||
return preserve_session_id;
|
||||
}
|
||||
|
||||
virtual void stop() override
|
||||
{
|
||||
if (!halt)
|
||||
@@ -454,6 +459,9 @@ namespace openvpn {
|
||||
os << reason;
|
||||
else
|
||||
os << "client was disconnected from server";
|
||||
disconnect_type = DT_HALT_RESTART;
|
||||
disconnect_in(Time::Duration::seconds(1));
|
||||
preserve_session_id = false;
|
||||
break;
|
||||
case HaltRestart::RESTART:
|
||||
ts = "RESTART";
|
||||
@@ -462,6 +470,9 @@ namespace openvpn {
|
||||
os << reason;
|
||||
else
|
||||
os << "server requested a client reconnect";
|
||||
disconnect_type = DT_HALT_RESTART;
|
||||
disconnect_in(Time::Duration::seconds(1));
|
||||
preserve_session_id = false;
|
||||
break;
|
||||
case HaltRestart::RESTART_PASSIVE:
|
||||
ts = "RESTART_PASSIVE";
|
||||
@@ -478,12 +489,17 @@ namespace openvpn {
|
||||
os << reason;
|
||||
else
|
||||
os << "server requested a client reconnect";
|
||||
disconnect_type = DT_HALT_RESTART;
|
||||
disconnect_in(Time::Duration::seconds(1));
|
||||
break;
|
||||
case HaltRestart::AUTH_FAILED:
|
||||
ts = "AUTH_FAILED";
|
||||
os << ts;
|
||||
if (tell_client && !reason.empty())
|
||||
os << ',' << reason;
|
||||
disconnect_type = DT_HALT_RESTART;
|
||||
disconnect_in(Time::Duration::seconds(1));
|
||||
preserve_session_id = false;
|
||||
break;
|
||||
case HaltRestart::RAW:
|
||||
{
|
||||
@@ -493,18 +509,15 @@ namespace openvpn {
|
||||
else
|
||||
ts = reason;
|
||||
os << reason;
|
||||
disconnect_type = DT_HALT_RESTART;
|
||||
disconnect_in(Time::Duration::seconds(1));
|
||||
preserve_session_id = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
OPENVPN_LOG("Disconnect: " << ts << ' ' << reason);
|
||||
|
||||
if (type != HaltRestart::RESTART_PASSIVE)
|
||||
{
|
||||
disconnect_type = DT_HALT_RESTART;
|
||||
disconnect_in(Time::Duration::seconds(1));
|
||||
}
|
||||
|
||||
if (Base::primary_defined())
|
||||
{
|
||||
buf->null_terminate();
|
||||
@@ -704,8 +717,6 @@ namespace openvpn {
|
||||
|
||||
openvpn_io::io_context& io_context;
|
||||
|
||||
bool halt = false;
|
||||
|
||||
// higher values are higher priority
|
||||
enum DisconnectType {
|
||||
DT_NONE=0,
|
||||
@@ -714,6 +725,9 @@ namespace openvpn {
|
||||
DT_HALT_RESTART,
|
||||
};
|
||||
int disconnect_type = DT_NONE;
|
||||
bool preserve_session_id = true;
|
||||
|
||||
bool halt = false;
|
||||
|
||||
PeerAddr::Ptr peer_addr;
|
||||
|
||||
|
||||
@@ -384,7 +384,7 @@ namespace openvpn {
|
||||
if (string::starts_with(dev_type, "tun"))
|
||||
layer = Layer(Layer::OSI_LAYER_3);
|
||||
else if (string::starts_with(dev_type, "tap"))
|
||||
layer = Layer(Layer::OSI_LAYER_2);
|
||||
throw proto_option_error("TAP mode is not supported");
|
||||
else
|
||||
throw proto_option_error("bad dev-type");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,242 @@
|
||||
// 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 <cstring>
|
||||
#include <memory>
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/hash.hpp>
|
||||
#include <openvpn/common/base64.hpp>
|
||||
#include <openvpn/common/string.hpp>
|
||||
#include <openvpn/buffer/buffer.hpp>
|
||||
#include <openvpn/random/randapi.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
class OpenSSLContext;
|
||||
class MbedTLSContext;
|
||||
|
||||
// Abstract base class used to provide an interface for TLS
|
||||
// Session Ticket keying originally described by RFC 5077.
|
||||
class TLSSessionTicketBase
|
||||
{
|
||||
public:
|
||||
typedef std::unique_ptr<TLSSessionTicketBase> UPtr;
|
||||
|
||||
OPENVPN_EXCEPTION(sess_ticket_error);
|
||||
|
||||
enum Status {
|
||||
NO_TICKET,
|
||||
TICKET_AVAILABLE,
|
||||
TICKET_EXPIRING,
|
||||
};
|
||||
|
||||
class Name
|
||||
{
|
||||
public:
|
||||
static constexpr size_t SIZE = 16;
|
||||
|
||||
explicit Name(RandomAPI& rng)
|
||||
{
|
||||
rng.rand_bytes(value_, SIZE);
|
||||
}
|
||||
|
||||
explicit Name(const std::string& name_b64)
|
||||
{
|
||||
b64_to_key(name_b64, "key name", value_, SIZE);
|
||||
}
|
||||
|
||||
explicit Name(const unsigned char name[SIZE])
|
||||
{
|
||||
std::memcpy(value_, name, SIZE);
|
||||
}
|
||||
|
||||
bool operator==(const Name& rhs) const
|
||||
{
|
||||
return std::memcmp(value_, rhs.value_, SIZE) == 0;
|
||||
}
|
||||
|
||||
bool operator!=(const Name& rhs) const
|
||||
{
|
||||
return std::memcmp(value_, rhs.value_, SIZE) != 0;
|
||||
}
|
||||
|
||||
bool operator<(const Name& rhs) const
|
||||
{
|
||||
return std::memcmp(value_, rhs.value_, SIZE) < 0;
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
return "TLSTicketName[" + b64() + ']';
|
||||
}
|
||||
|
||||
std::string b64() const
|
||||
{
|
||||
return base64->encode(value_, SIZE);
|
||||
}
|
||||
|
||||
template <typename HASH>
|
||||
void hash(HASH& h) const
|
||||
{
|
||||
h(value_, SIZE);
|
||||
}
|
||||
|
||||
#ifdef HAVE_CITYHASH
|
||||
std::size_t hashval() const
|
||||
{
|
||||
HashSizeT h;
|
||||
hash(h);
|
||||
return h.value();
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
// we need to friend SSL implementation classes
|
||||
friend class OpenSSLContext;
|
||||
friend class MbedTLSContext;
|
||||
|
||||
Name() {} // note that default constructor leaves object in an undefined state
|
||||
|
||||
unsigned char value_[SIZE];
|
||||
};
|
||||
|
||||
class Key
|
||||
{
|
||||
public:
|
||||
static constexpr size_t CIPHER_KEY_SIZE = 32;
|
||||
static constexpr size_t HMAC_KEY_SIZE = 16;
|
||||
|
||||
explicit Key(RandomAPI& rng)
|
||||
{
|
||||
rng.assert_crypto();
|
||||
rng.rand_bytes(cipher_value_, CIPHER_KEY_SIZE);
|
||||
rng.rand_bytes(hmac_value_, HMAC_KEY_SIZE);
|
||||
}
|
||||
|
||||
explicit Key(const std::string& cipher_key_b64, const std::string& hmac_key_b64)
|
||||
{
|
||||
b64_to_key(cipher_key_b64, "cipher key", cipher_value_, CIPHER_KEY_SIZE);
|
||||
b64_to_key(hmac_key_b64, "hmac key", hmac_value_, HMAC_KEY_SIZE);
|
||||
}
|
||||
|
||||
~Key()
|
||||
{
|
||||
// wipe keys
|
||||
std::memset(cipher_value_, 0, CIPHER_KEY_SIZE);
|
||||
std::memset(hmac_value_, 0, HMAC_KEY_SIZE);
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
return "TLSTicketKey[cipher=" + cipher_b64() + " hmac=" + hmac_b64() + ']';
|
||||
}
|
||||
|
||||
std::string cipher_b64() const
|
||||
{
|
||||
return base64->encode(cipher_value_, CIPHER_KEY_SIZE);
|
||||
}
|
||||
|
||||
std::string hmac_b64() const
|
||||
{
|
||||
return base64->encode(hmac_value_, HMAC_KEY_SIZE);
|
||||
}
|
||||
|
||||
bool operator==(const Key& rhs) const
|
||||
{
|
||||
return std::memcmp(cipher_value_, rhs.cipher_value_, CIPHER_KEY_SIZE) == 0 && std::memcmp(hmac_value_, rhs.hmac_value_, HMAC_KEY_SIZE) == 0;
|
||||
}
|
||||
|
||||
bool operator!=(const Key& rhs) const
|
||||
{
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
template <typename KEY_TRANSFORM>
|
||||
void key_transform(KEY_TRANSFORM& t)
|
||||
{
|
||||
unsigned char out[KEY_TRANSFORM::MAX_HMAC_SIZE];
|
||||
|
||||
// cipher
|
||||
{
|
||||
t.cipher_transform.reset();
|
||||
t.cipher_transform.update(cipher_value_, CIPHER_KEY_SIZE);
|
||||
const size_t size = t.cipher_transform.final(out);
|
||||
if (size < CIPHER_KEY_SIZE)
|
||||
throw sess_ticket_error("insufficient key material for cipher transform");
|
||||
std::memcpy(cipher_value_, out, CIPHER_KEY_SIZE);
|
||||
}
|
||||
|
||||
// hmac
|
||||
{
|
||||
t.hmac_transform.reset();
|
||||
t.hmac_transform.update(hmac_value_, HMAC_KEY_SIZE);
|
||||
const size_t size = t.hmac_transform.final(out);
|
||||
if (size < HMAC_KEY_SIZE)
|
||||
throw sess_ticket_error("insufficient key material for hmac transform");
|
||||
std::memcpy(hmac_value_, out, HMAC_KEY_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// we need to friend SSL implementation classes
|
||||
friend class OpenSSLContext;
|
||||
friend class MbedTLSContext;
|
||||
|
||||
Key() {} // note that default constructor leaves object in an undefined state
|
||||
|
||||
unsigned char cipher_value_[CIPHER_KEY_SIZE];
|
||||
unsigned char hmac_value_[HMAC_KEY_SIZE];
|
||||
};
|
||||
|
||||
// method returns name and key
|
||||
virtual Status create_session_ticket_key(Name& name, Key& key) const = 0;
|
||||
|
||||
// method is given name and returns key
|
||||
virtual Status lookup_session_ticket_key(const Name& name, Key& key) const = 0;
|
||||
|
||||
// return string that identifies the app
|
||||
virtual std::string session_id_context() const = 0;
|
||||
|
||||
virtual ~TLSSessionTicketBase() {}
|
||||
|
||||
private:
|
||||
static void b64_to_key(const std::string& b64, const char *title, unsigned char *out, const size_t outlen)
|
||||
{
|
||||
Buffer srcbuf(out, outlen, false);
|
||||
try {
|
||||
base64->decode(srcbuf, b64);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
throw sess_ticket_error(std::string("base64 decode for ") + title + ": " + std::string(e.what()));
|
||||
}
|
||||
if (srcbuf.size() != outlen)
|
||||
throw sess_ticket_error(std::string("wrong input size for ") + title + ", actual=" + std::to_string(srcbuf.size()) + " expected=" + std::to_string(outlen));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#ifdef HAVE_CITYHASH
|
||||
OPENVPN_HASH_METHOD(openvpn::TLSSessionTicketBase::Name, hashval);
|
||||
#endif
|
||||
@@ -41,6 +41,7 @@
|
||||
#include <openvpn/ssl/tlsver.hpp>
|
||||
#include <openvpn/ssl/tls_remote.hpp>
|
||||
#include <openvpn/ssl/tls_cert_profile.hpp>
|
||||
#include <openvpn/ssl/sess_ticket.hpp>
|
||||
#include <openvpn/random/randapi.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
@@ -65,7 +66,9 @@ namespace openvpn {
|
||||
virtual bool read_ciphertext_ready() const = 0;
|
||||
virtual BufferPtr read_ciphertext() = 0;
|
||||
virtual std::string ssl_handshake_details() const = 0;
|
||||
virtual const AuthCert::Ptr& auth_cert() const = 0;
|
||||
virtual bool did_full_handshake() = 0;
|
||||
virtual const AuthCert::Ptr& auth_cert() = 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
|
||||
{
|
||||
return tls_warnings;
|
||||
@@ -87,8 +90,9 @@ namespace openvpn {
|
||||
// create a new SSLAPI instance
|
||||
virtual SSLAPI::Ptr ssl() = 0;
|
||||
|
||||
// like ssl() above but verify hostname against cert CommonName and/or SubjectAltName
|
||||
virtual SSLAPI::Ptr ssl(const std::string& hostname) = 0;
|
||||
// like ssl() above but optionally verify hostname against cert CommonName and/or
|
||||
// SubjectAltName, and optionally set/lookup a cache key for this session.
|
||||
virtual SSLAPI::Ptr ssl(const std::string* hostname, const std::string* cache_key) = 0;
|
||||
|
||||
// client or server?
|
||||
virtual const Mode& mode() const = 0;
|
||||
@@ -140,6 +144,8 @@ namespace openvpn {
|
||||
virtual void set_mode(const Mode& mode_arg) = 0;
|
||||
virtual const Mode& get_mode() const = 0;
|
||||
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_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;
|
||||
@@ -166,7 +172,6 @@ namespace openvpn {
|
||||
virtual void set_tls_cert_profile(const TLSCertProfile::Type type) = 0;
|
||||
virtual void set_tls_cert_profile_override(const std::string& override) = 0;
|
||||
virtual void set_local_cert_enabled(const bool v) = 0;
|
||||
virtual void set_enable_renegotiation(const bool v) = 0;
|
||||
virtual void set_force_aes_cbc_ciphersuites(const bool v) = 0;
|
||||
virtual void set_x509_track(X509Track::ConfigSet x509_track_config_arg) = 0;
|
||||
virtual void set_rng(const RandomAPI::Ptr& rng_arg) = 0;
|
||||
|
||||
@@ -58,8 +58,11 @@ namespace openvpn {
|
||||
// purpose set to server.
|
||||
SERVER_TO_SERVER=(1<<4),
|
||||
|
||||
// Peer certificate is optional
|
||||
PEER_CERT_OPTIONAL=(1<<5),
|
||||
|
||||
// last flag marker
|
||||
LAST=(1<<5),
|
||||
LAST=(1<<6),
|
||||
};
|
||||
|
||||
// filter all but SSL flags
|
||||
|
||||
@@ -36,7 +36,8 @@ namespace openvpn {
|
||||
UNDEF=0,
|
||||
V1_0,
|
||||
V1_1,
|
||||
V1_2
|
||||
V1_2,
|
||||
V1_3
|
||||
};
|
||||
|
||||
inline const std::string to_string(const Type version)
|
||||
@@ -51,6 +52,8 @@ namespace openvpn {
|
||||
return "V1_1";
|
||||
case V1_2:
|
||||
return "V1_2";
|
||||
case V1_3:
|
||||
return "V1_3";
|
||||
default:
|
||||
return "???";
|
||||
}
|
||||
@@ -65,7 +68,9 @@ namespace openvpn {
|
||||
else if (ver == "1.1" && V1_1 <= max_version)
|
||||
return V1_1;
|
||||
else if (ver == "1.2" && V1_2 <= max_version)
|
||||
return V1_2;
|
||||
return V1_2;
|
||||
else if (ver == "1.3" && V1_3 <= max_version)
|
||||
return V1_2;
|
||||
else if (or_highest)
|
||||
return max_version;
|
||||
else
|
||||
@@ -99,6 +104,8 @@ namespace openvpn {
|
||||
tvm = V1_1;
|
||||
else if (override == "tls_1_2")
|
||||
tvm = V1_2;
|
||||
else if (override == "tls_1_3")
|
||||
tvm = V1_3;
|
||||
else
|
||||
throw option_error("tls-version-min: unrecognized override string");
|
||||
|
||||
|
||||
@@ -219,7 +219,7 @@ namespace openvpn {
|
||||
friend LinkImpl::Base; // calls tcp_read_handler
|
||||
|
||||
public:
|
||||
virtual void transport_start()
|
||||
void transport_start() override
|
||||
{
|
||||
if (!impl)
|
||||
{
|
||||
@@ -251,17 +251,17 @@ namespace openvpn {
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool transport_send_const(const Buffer& buf)
|
||||
bool transport_send_const(const Buffer& buf) override
|
||||
{
|
||||
return send_const(buf);
|
||||
}
|
||||
|
||||
virtual bool transport_send(BufferAllocated& buf)
|
||||
bool transport_send(BufferAllocated& buf) override
|
||||
{
|
||||
return send(buf);
|
||||
}
|
||||
|
||||
virtual bool transport_send_queue_empty()
|
||||
bool transport_send_queue_empty() override
|
||||
{
|
||||
if (impl)
|
||||
return impl->send_queue_empty();
|
||||
@@ -269,14 +269,14 @@ namespace openvpn {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool transport_has_send_queue()
|
||||
bool transport_has_send_queue() override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void transport_stop_requeueing() { }
|
||||
void transport_stop_requeueing() override { }
|
||||
|
||||
virtual unsigned int transport_send_queue_size()
|
||||
unsigned int transport_send_queue_size() override
|
||||
{
|
||||
if (impl)
|
||||
return impl->send_queue_size();
|
||||
@@ -284,13 +284,13 @@ namespace openvpn {
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual void reset_align_adjust(const size_t align_adjust)
|
||||
void reset_align_adjust(const size_t align_adjust) override
|
||||
{
|
||||
if (impl)
|
||||
impl->reset_align_adjust(align_adjust);
|
||||
}
|
||||
|
||||
virtual void server_endpoint_info(std::string& host, std::string& port, std::string& proto, std::string& ip_addr) const
|
||||
void server_endpoint_info(std::string& host, std::string& port, std::string& proto, std::string& ip_addr) const override
|
||||
{
|
||||
host = server_host;
|
||||
port = server_port;
|
||||
@@ -301,12 +301,12 @@ namespace openvpn {
|
||||
ip_addr = addr.to_string();
|
||||
}
|
||||
|
||||
virtual IP::Addr server_endpoint_addr() const
|
||||
IP::Addr server_endpoint_addr() const override
|
||||
{
|
||||
return IP::Addr::from_asio(server_endpoint.address());
|
||||
}
|
||||
|
||||
virtual Protocol transport_protocol() const
|
||||
Protocol transport_protocol() const override
|
||||
{
|
||||
if (server_endpoint.address().is_v4())
|
||||
return Protocol(Protocol::TCPv4);
|
||||
@@ -316,7 +316,7 @@ namespace openvpn {
|
||||
return Protocol();
|
||||
}
|
||||
|
||||
virtual void stop() { stop_(); }
|
||||
void stop() override { stop_(); }
|
||||
virtual ~Client() { stop_(); }
|
||||
|
||||
private:
|
||||
@@ -350,7 +350,7 @@ namespace openvpn {
|
||||
{
|
||||
}
|
||||
|
||||
virtual void transport_reparent(TransportClientParent* parent_arg)
|
||||
void transport_reparent(TransportClientParent* parent_arg) override
|
||||
{
|
||||
parent = parent_arg;
|
||||
}
|
||||
@@ -908,7 +908,6 @@ namespace openvpn {
|
||||
proxy_remote_list().get_endpoint(server_endpoint);
|
||||
OPENVPN_LOG("Contacting " << server_endpoint << " via HTTP Proxy");
|
||||
parent->transport_wait_proxy();
|
||||
parent->ip_hole_punch(server_endpoint_addr());
|
||||
socket.open(server_endpoint.protocol());
|
||||
#ifdef OPENVPN_PLATFORM_TYPE_UNIX
|
||||
if (config->socket_protect)
|
||||
|
||||
@@ -117,8 +117,6 @@ namespace openvpn {
|
||||
OPENVPN_LOG("TransportRelayFactory: Proxy Error in null parent: " << Error::name(fatal_err) << " : " << err_text);
|
||||
}
|
||||
|
||||
virtual void ip_hole_punch(const IP::Addr& addr) {}
|
||||
|
||||
// Return true if we are transporting OpenVPN protocol
|
||||
virtual bool transport_is_openvpn_protocol() { return is_openvpn_protocol; }
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ namespace openvpn {
|
||||
friend LinkImpl::Base; // calls tcp_read_handler
|
||||
|
||||
public:
|
||||
virtual void transport_start()
|
||||
void transport_start() override
|
||||
{
|
||||
if (!impl)
|
||||
{
|
||||
@@ -108,17 +108,17 @@ namespace openvpn {
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool transport_send_const(const Buffer& buf)
|
||||
bool transport_send_const(const Buffer& buf) override
|
||||
{
|
||||
return send_const(buf);
|
||||
}
|
||||
|
||||
virtual bool transport_send(BufferAllocated& buf)
|
||||
bool transport_send(BufferAllocated& buf) override
|
||||
{
|
||||
return send(buf);
|
||||
}
|
||||
|
||||
virtual bool transport_send_queue_empty()
|
||||
bool transport_send_queue_empty() override
|
||||
{
|
||||
if (impl)
|
||||
return impl->send_queue_empty();
|
||||
@@ -126,12 +126,12 @@ namespace openvpn {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool transport_has_send_queue()
|
||||
bool transport_has_send_queue() override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual unsigned int transport_send_queue_size()
|
||||
unsigned int transport_send_queue_size() override
|
||||
{
|
||||
if (impl)
|
||||
return impl->send_queue_size();
|
||||
@@ -139,13 +139,13 @@ namespace openvpn {
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual void reset_align_adjust(const size_t align_adjust)
|
||||
void reset_align_adjust(const size_t align_adjust) override
|
||||
{
|
||||
if (impl)
|
||||
impl->reset_align_adjust(align_adjust);
|
||||
}
|
||||
|
||||
virtual void server_endpoint_info(std::string& host, std::string& port, std::string& proto, std::string& ip_addr) const
|
||||
void server_endpoint_info(std::string& host, std::string& port, std::string& proto, std::string& ip_addr) const override
|
||||
{
|
||||
host = server_host;
|
||||
port = server_port;
|
||||
@@ -154,18 +154,18 @@ namespace openvpn {
|
||||
ip_addr = addr.to_string();
|
||||
}
|
||||
|
||||
virtual IP::Addr server_endpoint_addr() const
|
||||
IP::Addr server_endpoint_addr() const override
|
||||
{
|
||||
return IP::Addr::from_asio(server_endpoint.address());
|
||||
}
|
||||
|
||||
virtual Protocol transport_protocol() const
|
||||
Protocol transport_protocol() const override
|
||||
{
|
||||
return server_protocol;
|
||||
}
|
||||
|
||||
virtual void stop() { stop_(); }
|
||||
virtual ~Client() { stop_(); }
|
||||
void stop() override { stop_(); }
|
||||
~Client() override { stop_(); }
|
||||
|
||||
private:
|
||||
Client(openvpn_io::io_context& io_context_arg,
|
||||
@@ -182,12 +182,12 @@ namespace openvpn {
|
||||
{
|
||||
}
|
||||
|
||||
virtual void transport_reparent(TransportClientParent* parent_arg)
|
||||
void transport_reparent(TransportClientParent* parent_arg) override
|
||||
{
|
||||
parent = parent_arg;
|
||||
}
|
||||
|
||||
virtual void transport_stop_requeueing()
|
||||
void transport_stop_requeueing() override
|
||||
{
|
||||
stop_requeueing = true;
|
||||
}
|
||||
@@ -280,7 +280,6 @@ namespace openvpn {
|
||||
OPENVPN_LOG("Contacting " << server_endpoint << " via "
|
||||
<< server_protocol.str());
|
||||
parent->transport_wait();
|
||||
parent->ip_hole_punch(server_endpoint_addr());
|
||||
socket.open(server_endpoint.protocol());
|
||||
#if defined(OPENVPN_PLATFORM_TYPE_UNIX) || defined(OPENVPN_PLATFORM_UWP)
|
||||
if (config->socket_protect)
|
||||
@@ -315,7 +314,8 @@ namespace openvpn {
|
||||
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);
|
||||
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));
|
||||
|
||||
@@ -70,11 +70,6 @@ namespace openvpn {
|
||||
virtual void transport_error(const Error::Type fatal_err, const std::string& err_text) = 0;
|
||||
virtual void proxy_error(const Error::Type fatal_err, const std::string& err_text) = 0;
|
||||
|
||||
// Called just prior to transport layer opening up a socket to addr.
|
||||
// Allows the implementation to ensure connectivity for outgoing
|
||||
// transport connection to server.
|
||||
virtual void ip_hole_punch(const IP::Addr& addr) = 0;
|
||||
|
||||
// Return true if we are transporting OpenVPN protocol
|
||||
virtual bool transport_is_openvpn_protocol() = 0;
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ namespace openvpn {
|
||||
typedef Link<Client*> LinkImpl;
|
||||
|
||||
public:
|
||||
virtual void transport_start()
|
||||
void transport_start() override
|
||||
{
|
||||
if (!impl)
|
||||
{
|
||||
@@ -111,40 +111,40 @@ namespace openvpn {
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool transport_send_const(const Buffer& buf)
|
||||
bool transport_send_const(const Buffer& buf) override
|
||||
{
|
||||
return send(buf);
|
||||
}
|
||||
|
||||
virtual bool transport_send(BufferAllocated& buf)
|
||||
bool transport_send(BufferAllocated& buf) override
|
||||
{
|
||||
return send(buf);
|
||||
}
|
||||
|
||||
virtual bool transport_send_queue_empty() // really only has meaning for TCP
|
||||
bool transport_send_queue_empty() override // really only has meaning for TCP
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool transport_has_send_queue()
|
||||
bool transport_has_send_queue() override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void transport_stop_requeueing() { }
|
||||
void transport_stop_requeueing() override { }
|
||||
|
||||
virtual unsigned int transport_send_queue_size()
|
||||
unsigned int transport_send_queue_size() override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual void reset_align_adjust(const size_t align_adjust)
|
||||
void reset_align_adjust(const size_t align_adjust) override
|
||||
{
|
||||
if (impl)
|
||||
impl->reset_align_adjust(align_adjust);
|
||||
}
|
||||
|
||||
virtual void server_endpoint_info(std::string& host, std::string& port, std::string& proto, std::string& ip_addr) const
|
||||
void server_endpoint_info(std::string& host, std::string& port, std::string& proto, std::string& ip_addr) const override
|
||||
{
|
||||
host = server_host;
|
||||
port = server_port;
|
||||
@@ -154,12 +154,12 @@ namespace openvpn {
|
||||
ip_addr = addr.to_string();
|
||||
}
|
||||
|
||||
virtual IP::Addr server_endpoint_addr() const
|
||||
IP::Addr server_endpoint_addr() const override
|
||||
{
|
||||
return IP::Addr::from_asio(server_endpoint.address());
|
||||
}
|
||||
|
||||
virtual Protocol transport_protocol() const
|
||||
Protocol transport_protocol() const override
|
||||
{
|
||||
if (server_endpoint.address().is_v4())
|
||||
return Protocol(Protocol::UDPv4);
|
||||
@@ -169,8 +169,8 @@ namespace openvpn {
|
||||
return Protocol();
|
||||
}
|
||||
|
||||
virtual void stop() { stop_(); }
|
||||
virtual ~Client() { stop_(); }
|
||||
void stop() override { stop_(); }
|
||||
~Client() override { stop_(); }
|
||||
|
||||
private:
|
||||
Client(openvpn_io::io_context& io_context_arg,
|
||||
@@ -185,7 +185,7 @@ namespace openvpn {
|
||||
{
|
||||
}
|
||||
|
||||
virtual void transport_reparent(TransportClientParent* parent_arg)
|
||||
void transport_reparent(TransportClientParent* parent_arg) override
|
||||
{
|
||||
parent = parent_arg;
|
||||
}
|
||||
@@ -265,7 +265,6 @@ namespace openvpn {
|
||||
config->remote_list->get_endpoint(server_endpoint);
|
||||
OPENVPN_LOG("Contacting " << server_endpoint << " via UDP");
|
||||
parent->transport_wait();
|
||||
parent->ip_hole_punch(server_endpoint_addr());
|
||||
socket.open(server_endpoint.protocol());
|
||||
#if defined(OPENVPN_PLATFORM_TYPE_UNIX) || defined(OPENVPN_PLATFORM_UWP)
|
||||
if (config->socket_protect)
|
||||
|
||||
@@ -228,6 +228,15 @@ namespace openvpn {
|
||||
return true;
|
||||
}
|
||||
|
||||
// When the exclude local network option is enabled this
|
||||
// function is called to get a list of local networks so routes
|
||||
// to exclude them from the VPN network are generated
|
||||
// This should be a list of CIDR networks (e.g. 192.168.0.0/24)
|
||||
virtual const std::vector<std::string> tun_builder_get_local_networks(bool ipv6)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
// Indicates a reconnection with persisted tun state.
|
||||
virtual void tun_builder_establish_lite()
|
||||
{
|
||||
|
||||
@@ -97,11 +97,11 @@ namespace openvpn {
|
||||
return new ClientConfig;
|
||||
}
|
||||
|
||||
virtual TunClient::Ptr new_tun_client_obj(openvpn_io::io_context& io_context,
|
||||
TunClient::Ptr new_tun_client_obj(openvpn_io::io_context& io_context,
|
||||
TunClientParent& parent,
|
||||
TransportClient* transcli);
|
||||
TransportClient* transcli) override;
|
||||
|
||||
virtual void finalize(const bool disconnected)
|
||||
void finalize(const bool disconnected) override
|
||||
{
|
||||
if (disconnected)
|
||||
tun_persist.reset();
|
||||
|
||||
@@ -36,6 +36,7 @@ namespace openvpn {
|
||||
virtual void add_route(const bool add, const IP::Addr& addr, const int prefix_len) = 0;
|
||||
virtual bool enabled(const IPVerFlags& ipv) const = 0;
|
||||
virtual void emulate(TunBuilderBase* tb, IPVerFlags& ipv, const IP::Addr& server_addr) const = 0;
|
||||
virtual void add_default_routes(bool ipv4, bool ipv6) = 0;
|
||||
};
|
||||
|
||||
struct EmulateExcludeRouteFactory : public RC<thread_unsafe_refcount>
|
||||
|
||||
@@ -85,11 +85,6 @@ namespace openvpn {
|
||||
// true: this is the final disconnect, or
|
||||
// false: we are in a pause/reconnecting state.
|
||||
virtual void finalize(const bool disconnected) {}
|
||||
|
||||
// Called just prior to transport layer opening up a socket to addr.
|
||||
// Allows the implementation to ensure connectivity for outgoing
|
||||
// transport connection to server.
|
||||
virtual void ip_hole_punch(const IP::Addr& addr) {}
|
||||
};
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
@@ -63,6 +63,7 @@ namespace openvpn {
|
||||
std::string session_name;
|
||||
int mtu = 0;
|
||||
bool google_dns_fallback = false;
|
||||
bool allow_local_lan_access = false;
|
||||
Layer layer{Layer::OSI_LAYER_3};
|
||||
|
||||
// If remote_bypass is true, obtain cached remote IPs from
|
||||
@@ -126,15 +127,43 @@ namespace openvpn {
|
||||
add_remote_bypass_routes(tb, *config.remote_list, server_addr, eer.get(), quiet);
|
||||
|
||||
// add routes
|
||||
add_routes(tb, opt, server_addr, ipv, eer.get(), quiet);
|
||||
if (config.allow_local_lan_access)
|
||||
{
|
||||
// query local lan exclude routes and then
|
||||
// copy option list to construct a copy with the excluded routes as route options
|
||||
OptionList excludedRoutesOptions = opt;
|
||||
for (const std::string& exRoute: tb->tun_builder_get_local_networks(false))
|
||||
{
|
||||
excludedRoutesOptions.add_item(Option{"route", exRoute, "", "net_gateway"});
|
||||
}
|
||||
|
||||
// emulate exclude routes
|
||||
if (eer && eer->enabled(ipv))
|
||||
eer->emulate(tb, ipv, server_addr);
|
||||
for (const std::string& exRoute: tb->tun_builder_get_local_networks(true))
|
||||
{
|
||||
excludedRoutesOptions.add_item(Option{"route-ipv6", exRoute, "", "net_gateway"});
|
||||
}
|
||||
|
||||
// configure redirect-gateway
|
||||
if (!tb->tun_builder_reroute_gw(ipv.rgv4(), ipv.rgv6(), ipv.api_flags()))
|
||||
throw tun_prop_route_error("tun_builder_reroute_gw for redirect-gateway failed");
|
||||
add_routes(tb, excludedRoutesOptions, ipv, eer.get(), quiet);
|
||||
}
|
||||
else
|
||||
{
|
||||
add_routes(tb, opt, ipv, eer.get(), quiet);
|
||||
}
|
||||
|
||||
|
||||
if (eer)
|
||||
{
|
||||
// Route emulation needs to know if default routes are included
|
||||
// from redirect-gateway
|
||||
eer->add_default_routes(ipv.rgv4(), ipv.rgv6());
|
||||
// emulate exclude routes
|
||||
eer->emulate(tb, ipv, server_addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// configure redirect-gateway
|
||||
if (!tb->tun_builder_reroute_gw(ipv.rgv4(), ipv.rgv6(), ipv.api_flags()))
|
||||
throw tun_prop_route_error("tun_builder_reroute_gw for redirect-gateway failed");
|
||||
}
|
||||
|
||||
// add DNS servers and domain prefixes
|
||||
const unsigned int dhcp_option_flags = add_dhcp_options(tb, opt, quiet);
|
||||
@@ -335,7 +364,9 @@ namespace openvpn {
|
||||
EmulateExcludeRoute* eer)
|
||||
{
|
||||
const std::string addr_str = addr.to_string();
|
||||
if (add)
|
||||
if (eer)
|
||||
eer->add_route(add, addr, prefix_length);
|
||||
else if (add)
|
||||
{
|
||||
if (!tb->tun_builder_add_route(addr_str, prefix_length, metric, ipv6))
|
||||
throw tun_prop_route_error("tun_builder_add_route failed");
|
||||
@@ -345,8 +376,7 @@ namespace openvpn {
|
||||
if (!tb->tun_builder_exclude_route(addr_str, prefix_length, metric, ipv6))
|
||||
throw tun_prop_route_error("tun_builder_exclude_route failed");
|
||||
}
|
||||
if (eer)
|
||||
eer->add_route(add, addr, prefix_length);
|
||||
|
||||
}
|
||||
|
||||
// Check the target of a route.
|
||||
@@ -369,7 +399,6 @@ namespace openvpn {
|
||||
|
||||
static void add_routes(TunBuilderBase* tb,
|
||||
const OptionList& opt,
|
||||
const IP::Addr& server_addr,
|
||||
const IPVerFlags& ipv,
|
||||
EmulateExcludeRoute* eer,
|
||||
const bool quiet)
|
||||
|
||||
@@ -325,6 +325,11 @@ namespace openvpn {
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (h->nlmsg_type == NLMSG_DONE)
|
||||
{
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (h->nlmsg_type == NLMSG_ERROR)
|
||||
{
|
||||
err = (struct nlmsgerr *)NLMSG_DATA(h);
|
||||
@@ -339,7 +344,11 @@ namespace openvpn {
|
||||
{
|
||||
ret = 0;
|
||||
if (cb)
|
||||
{
|
||||
ret = cb(h, arg_cb);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -355,7 +364,6 @@ namespace openvpn {
|
||||
if (cb)
|
||||
{
|
||||
ret = cb(h, arg_cb);
|
||||
goto out;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -379,6 +387,10 @@ namespace openvpn {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
// continue reading multipart message
|
||||
if (!(h->nlmsg_flags & NLM_F_MULTI))
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
close(fd);
|
||||
@@ -392,6 +404,10 @@ out:
|
||||
sa_family_t family;
|
||||
IP::Addr gw;
|
||||
std::string iface;
|
||||
std::string iface_to_ignore;
|
||||
int metric;
|
||||
IP::Route dst;
|
||||
int prefix_len;
|
||||
} route_res_t;
|
||||
|
||||
static int
|
||||
@@ -402,6 +418,20 @@ out:
|
||||
struct rtattr *rta = RTM_RTA(r);
|
||||
int len = n->nlmsg_len - NLMSG_LENGTH(sizeof(*r));
|
||||
int ifindex = 0;
|
||||
int metric = 0;
|
||||
|
||||
IP::Addr gw;
|
||||
|
||||
IP::Route route;
|
||||
switch (res->family)
|
||||
{
|
||||
case AF_INET:
|
||||
route = IP::Route("0.0.0.0/0");
|
||||
break;
|
||||
case AF_INET6:
|
||||
route = IP::Route("::/0");
|
||||
break;
|
||||
}
|
||||
|
||||
while (RTA_OK(rta, len))
|
||||
{
|
||||
@@ -413,7 +443,21 @@ out:
|
||||
break;
|
||||
case RTA_DST:
|
||||
/* route prefix */
|
||||
RTA_DATA(rta);
|
||||
{
|
||||
const unsigned char *bytestr = (unsigned char *)RTA_DATA(rta);
|
||||
switch (res->family)
|
||||
{
|
||||
case AF_INET:
|
||||
route = IP::Route(IPv4::Addr::from_bytes_net(bytestr).to_string() + "/" + std::to_string(r->rtm_dst_len));
|
||||
break;
|
||||
case AF_INET6:
|
||||
route = IP::Route(IPv6::Addr::from_byte_string(bytestr).to_string() + "/" + std::to_string(r->rtm_dst_len));
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RTA_PRIORITY:
|
||||
metric = *(unsigned int *)RTA_DATA(rta);
|
||||
break;
|
||||
case RTA_GATEWAY:
|
||||
/* GW for the route */
|
||||
@@ -422,10 +466,10 @@ out:
|
||||
switch (res->family)
|
||||
{
|
||||
case AF_INET:
|
||||
res->gw = IP::Addr::from_ipv4(IPv4::Addr::from_bytes_net(bytestr));
|
||||
gw = IP::Addr::from_ipv4(IPv4::Addr::from_bytes_net(bytestr));
|
||||
break;
|
||||
case AF_INET6:
|
||||
res->gw = IP::Addr::from_ipv6(IPv6::Addr::from_byte_string(bytestr));
|
||||
gw = IP::Addr::from_ipv6(IPv6::Addr::from_byte_string(bytestr));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -435,34 +479,86 @@ out:
|
||||
rta = RTA_NEXT(rta, len);
|
||||
}
|
||||
|
||||
if (ifindex > 0)
|
||||
if (!gw.defined() || ifindex <= 0)
|
||||
{
|
||||
char iface[IFNAMSIZ];
|
||||
if (!if_indextoname(ifindex, iface))
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": rtnl: can't get ifname for index "
|
||||
<< ifindex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
res->iface = iface;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
OPENVPN_LOG_RTNL(__func__ << ": RTA_GATEWAY " << gw.to_string());
|
||||
}
|
||||
|
||||
if (!route.contains(res->dst))
|
||||
{
|
||||
OPENVPN_LOG_RTNL(__func__ << ": Ignore gw for unmatched route " << route.to_string());
|
||||
return 0;
|
||||
}
|
||||
|
||||
char iface[IFNAMSIZ];
|
||||
if (!if_indextoname(ifindex, iface))
|
||||
{
|
||||
OPENVPN_LOG(__func__ << ": rtnl: can't get ifname for index "
|
||||
<< ifindex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (res->iface_to_ignore == iface)
|
||||
{
|
||||
OPENVPN_LOG_RTNL(__func__ << ": Ignore gw " << gw.to_string() << " on " << iface);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// skip if gw's route prefix is shorter
|
||||
if (r->rtm_dst_len < res->prefix_len)
|
||||
{
|
||||
OPENVPN_LOG_RTNL(__func__ << ": Ignore gw " << gw.to_string() << " with shorter route prefix " << route.to_string());
|
||||
return 0;
|
||||
}
|
||||
|
||||
// skip if gw's route metric is higher
|
||||
if ((metric > res->metric) && (res->metric != -1))
|
||||
{
|
||||
OPENVPN_LOG_RTNL(__func__ << ": Ignore gw " << gw.to_string() << " with higher metrics " << metric);
|
||||
return 0;
|
||||
}
|
||||
|
||||
res->iface = iface;
|
||||
res->gw = gw;
|
||||
res->metric = metric;
|
||||
res->prefix_len = res->prefix_len;
|
||||
|
||||
OPENVPN_LOG_RTNL(__func__ << ": Use gw " << gw.to_string() << " route " << route.to_string() << " metric " << metric);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for best gateway for a given route
|
||||
* @param iface_to_ignore this allows to exclude certain interface
|
||||
* from discovered gateways. Used when we want to exclude VPN interface
|
||||
* when there is active VPN connection with redirected default gateway
|
||||
* @param route route for which we search gw
|
||||
* @param [out] best_gw found gw
|
||||
* @param [out] best_iface network interface on which gw was found
|
||||
* @return
|
||||
*/
|
||||
static int
|
||||
sitnl_route_best_gw(const IP::Route& route, IP::Addr& best_gw,
|
||||
sitnl_route_best_gw(const std::string& iface_to_ignore,
|
||||
const IP::Route& route,
|
||||
IP::Addr& best_gw,
|
||||
std::string& best_iface)
|
||||
{
|
||||
struct sitnl_route_req req = { };
|
||||
route_res_t res;
|
||||
int ret = -EINVAL;
|
||||
|
||||
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.r));
|
||||
req.n.nlmsg_type = RTM_GETROUTE;
|
||||
req.n.nlmsg_flags = NLM_F_REQUEST;
|
||||
|
||||
route_res_t res;
|
||||
res.metric = -1;
|
||||
res.prefix_len = -1;
|
||||
|
||||
int ret = -EINVAL;
|
||||
|
||||
res.family = req.r.rtm_family = route.addr.family();
|
||||
req.r.rtm_dst_len = route.prefix_len;
|
||||
|
||||
@@ -471,6 +567,9 @@ out:
|
||||
req.n.nlmsg_flags |= NLM_F_DUMP;
|
||||
}
|
||||
|
||||
res.iface_to_ignore = iface_to_ignore;
|
||||
res.dst = route;
|
||||
|
||||
{
|
||||
unsigned char bytestr[IP::Addr::V6_SIZE / 8];
|
||||
route.addr.to_byte_string_variable(bytestr);
|
||||
@@ -675,7 +774,7 @@ err:
|
||||
const std::string& iface, const uint32_t table,
|
||||
const int metric)
|
||||
{
|
||||
return sitnl_route_set(RTM_NEWROUTE, NLM_F_CREATE | NLM_F_REPLACE, iface,
|
||||
return sitnl_route_set(RTM_NEWROUTE, NLM_F_CREATE, iface,
|
||||
route, gw,
|
||||
(enum rt_class_t)(!table ? RT_TABLE_MAIN : table),
|
||||
metric, RT_SCOPE_UNIVERSE, RTPROT_BOOT, RTN_UNICAST);
|
||||
@@ -696,14 +795,14 @@ err:
|
||||
|
||||
static int
|
||||
net_route_best_gw(const IP::Route6& route, IPv6::Addr& best_gw6,
|
||||
std::string& best_iface)
|
||||
std::string& best_iface, const std::string& iface_to_ignore = "")
|
||||
{
|
||||
IP::Addr best_gw;
|
||||
int ret;
|
||||
|
||||
OPENVPN_LOG(__func__ << " query IPv6: " << route);
|
||||
|
||||
ret = sitnl_route_best_gw(IP::Route(IP::Addr::from_ipv6(route.addr), route.prefix_len),
|
||||
ret = sitnl_route_best_gw(iface_to_ignore, IP::Route(IP::Addr::from_ipv6(route.addr), route.prefix_len),
|
||||
best_gw, best_iface);
|
||||
if (ret >= 0)
|
||||
{
|
||||
@@ -715,14 +814,14 @@ err:
|
||||
|
||||
static int
|
||||
net_route_best_gw(const IP::Route4& route, IPv4::Addr &best_gw4,
|
||||
std::string& best_iface)
|
||||
std::string& best_iface, const std::string& iface_to_ignore = "")
|
||||
{
|
||||
IP::Addr best_gw;
|
||||
int ret;
|
||||
|
||||
OPENVPN_LOG(__func__ << " query IPv4: " << route);
|
||||
|
||||
ret = sitnl_route_best_gw(IP::Route(IP::Addr::from_ipv4(route.addr), route.prefix_len),
|
||||
ret = sitnl_route_best_gw(iface_to_ignore, IP::Route(IP::Addr::from_ipv4(route.addr), route.prefix_len),
|
||||
best_gw, best_iface);
|
||||
if (ret >= 0)
|
||||
{
|
||||
|
||||
@@ -34,10 +34,10 @@
|
||||
// check if Netlink has been selected at compile time
|
||||
#ifdef OPENVPN_USE_SITNL
|
||||
#include <openvpn/tun/linux/client/tunnetlink.hpp>
|
||||
#define TUN_LINUX TunNetlink
|
||||
#define TUN_LINUX openvpn::TunNetlink::TunMethods
|
||||
#else
|
||||
#include <openvpn/tun/linux/client/tunsetup.hpp>
|
||||
#define TUN_LINUX TunLinux
|
||||
#include <openvpn/tun/linux/client/tuniproute.hpp>
|
||||
#define TUN_LINUX openvpn::TunIPRoute::TunMethods
|
||||
#endif
|
||||
|
||||
namespace openvpn {
|
||||
@@ -122,7 +122,7 @@ namespace openvpn {
|
||||
if (tun_setup_factory)
|
||||
return tun_setup_factory->new_setup_obj();
|
||||
else
|
||||
return new TUN_LINUX::Setup();
|
||||
return new TunLinuxSetup::Setup<TUN_LINUX>();
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -192,10 +192,11 @@ namespace openvpn {
|
||||
tun_setup = config->new_setup_obj();
|
||||
|
||||
// create config object for tun setup layer
|
||||
TUN_LINUX::Setup::Config tsconf;
|
||||
TunLinuxSetup::Setup<TUN_LINUX>::Config tsconf;
|
||||
tsconf.layer = config->tun_prop.layer;
|
||||
tsconf.dev_name = config->dev_name;
|
||||
tsconf.txqueuelen = config->txqueuelen;
|
||||
tsconf.add_bypass_routes_on_establish = true;
|
||||
|
||||
// open/config tun
|
||||
{
|
||||
|
||||
@@ -0,0 +1,351 @@
|
||||
// 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 <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <net/if.h>
|
||||
#include <linux/if_tun.h>
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/file.hpp>
|
||||
#include <openvpn/common/split.hpp>
|
||||
#include <openvpn/common/splitlines.hpp>
|
||||
#include <openvpn/common/hexstr.hpp>
|
||||
#include <openvpn/common/to_string.hpp>
|
||||
#include <openvpn/common/process.hpp>
|
||||
#include <openvpn/common/action.hpp>
|
||||
#include <openvpn/addr/route.hpp>
|
||||
#include <openvpn/tun/builder/capture.hpp>
|
||||
#include <openvpn/tun/builder/setup.hpp>
|
||||
#include <openvpn/tun/client/tunbase.hpp>
|
||||
#include <openvpn/tun/client/tunprop.hpp>
|
||||
#include <openvpn/tun/client/tunprop.hpp>
|
||||
#include <openvpn/tun/linux/client/tunsetup.hpp>
|
||||
#include <openvpn/netconf/linux/gw.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace TunIPRoute {
|
||||
|
||||
using namespace openvpn::TunLinuxSetup;
|
||||
|
||||
enum { // add_del_route flags
|
||||
R_IPv6=(1<<0),
|
||||
R_ADD_SYS=(1<<1),
|
||||
R_ADD_DCO=(1<<2),
|
||||
R_ADD_ALL=R_ADD_SYS|R_ADD_DCO,
|
||||
};
|
||||
|
||||
inline IP::Addr cvt_pnr_ip_v4(const std::string& hexaddr)
|
||||
{
|
||||
BufferAllocated v(4, BufferAllocated::CONSTRUCT_ZERO);
|
||||
parse_hex(v, hexaddr);
|
||||
if (v.size() != 4)
|
||||
throw tun_linux_error("bad hex address");
|
||||
IPv4::Addr ret = IPv4::Addr::from_bytes(v.data());
|
||||
return IP::Addr::from_ipv4(ret);
|
||||
}
|
||||
|
||||
inline void add_del_route(const std::string& addr_str,
|
||||
const int prefix_len,
|
||||
const std::string& gateway_str,
|
||||
const std::string& dev,
|
||||
const unsigned int flags,
|
||||
std::vector<IP::Route>* rtvec,
|
||||
Action::Ptr& create,
|
||||
Action::Ptr& destroy)
|
||||
{
|
||||
if (flags & R_IPv6)
|
||||
{
|
||||
const IPv6::Addr addr = IPv6::Addr::from_string(addr_str);
|
||||
const IPv6::Addr netmask = IPv6::Addr::netmask_from_prefix_len(prefix_len);
|
||||
const IPv6::Addr net = addr & netmask;
|
||||
|
||||
if (flags & R_ADD_SYS)
|
||||
{
|
||||
// ip route add 2001:db8:1::/48 via 2001:db8:1::1
|
||||
Command::Ptr add(new Command);
|
||||
add->argv.push_back("/sbin/ip");
|
||||
add->argv.push_back("-6");
|
||||
add->argv.push_back("route");
|
||||
add->argv.push_back("prepend");
|
||||
add->argv.push_back(net.to_string() + '/' + openvpn::to_string(prefix_len));
|
||||
add->argv.push_back("via");
|
||||
add->argv.push_back(gateway_str);
|
||||
if (!dev.empty())
|
||||
{
|
||||
add->argv.push_back("dev");
|
||||
add->argv.push_back(dev);
|
||||
}
|
||||
create = add;
|
||||
|
||||
// for the destroy command, copy the add command but replace "add" with "delete"
|
||||
Command::Ptr del(add->copy());
|
||||
del->argv[3] = "del";
|
||||
destroy = del;
|
||||
}
|
||||
|
||||
if (rtvec && (flags & R_ADD_DCO))
|
||||
rtvec->emplace_back(IP::Addr::from_ipv6(net), prefix_len);
|
||||
}
|
||||
else
|
||||
{
|
||||
const IPv4::Addr addr = IPv4::Addr::from_string(addr_str);
|
||||
const IPv4::Addr netmask = IPv4::Addr::netmask_from_prefix_len(prefix_len);
|
||||
const IPv4::Addr net = addr & netmask;
|
||||
|
||||
if (flags & R_ADD_SYS)
|
||||
{
|
||||
// ip route add 192.0.2.128/25 via 192.0.2.1
|
||||
Command::Ptr add(new Command);
|
||||
add->argv.push_back("/sbin/ip");
|
||||
add->argv.push_back("-4");
|
||||
add->argv.push_back("route");
|
||||
add->argv.push_back("prepend");
|
||||
add->argv.push_back(net.to_string() + '/' + openvpn::to_string(prefix_len));
|
||||
add->argv.push_back("via");
|
||||
add->argv.push_back(gateway_str);
|
||||
if (!dev.empty())
|
||||
{
|
||||
add->argv.push_back("dev");
|
||||
add->argv.push_back(dev);
|
||||
}
|
||||
create = add;
|
||||
|
||||
// for the destroy command, copy the add command but replace "add" with "delete"
|
||||
Command::Ptr del(add->copy());
|
||||
del->argv[3] = "del";
|
||||
destroy = del;
|
||||
}
|
||||
|
||||
if (rtvec && (flags & R_ADD_DCO))
|
||||
rtvec->emplace_back(IP::Addr::from_ipv4(net), prefix_len);
|
||||
}
|
||||
}
|
||||
|
||||
inline void add_del_route(const std::string& addr_str,
|
||||
const int prefix_len,
|
||||
const std::string& gateway_str,
|
||||
const std::string& dev,
|
||||
const unsigned int flags,// add interface route to rtvec if defined
|
||||
std::vector<IP::Route>* rtvec,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
{
|
||||
Action::Ptr c, d;
|
||||
add_del_route(addr_str, prefix_len, gateway_str, dev, flags, rtvec, c, d);
|
||||
create.add(c);
|
||||
destroy.add(d);
|
||||
}
|
||||
|
||||
inline void iface_up(const std::string& iface_name,
|
||||
const int mtu,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
{
|
||||
{
|
||||
Command::Ptr add(new Command);
|
||||
add->argv.push_back("/sbin/ip");
|
||||
add->argv.push_back("link");
|
||||
add->argv.push_back("set");
|
||||
add->argv.push_back(iface_name);
|
||||
add->argv.push_back("up");
|
||||
if (mtu > 0)
|
||||
{
|
||||
add->argv.push_back("mtu");
|
||||
add->argv.push_back(openvpn::to_string(mtu));
|
||||
}
|
||||
create.add(add);
|
||||
|
||||
// for the destroy command, copy the add command but replace "up" with "down"
|
||||
Command::Ptr del(add->copy());
|
||||
del->argv[4] = "down";
|
||||
destroy.add(del);
|
||||
}
|
||||
}
|
||||
|
||||
inline void iface_config(const std::string& iface_name,
|
||||
int unit,
|
||||
const TunBuilderCapture& pull,
|
||||
std::vector<IP::Route>* rtvec,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
{
|
||||
// set local4 and local6 to point to IPv4/6 route configurations
|
||||
const TunBuilderCapture::RouteAddress* local4 = pull.vpn_ipv4();
|
||||
const TunBuilderCapture::RouteAddress* local6 = pull.vpn_ipv6();
|
||||
|
||||
// Set IPv4 Interface
|
||||
if (local4)
|
||||
{
|
||||
Command::Ptr add(new Command);
|
||||
add->argv.push_back("/sbin/ip");
|
||||
add->argv.push_back("-4");
|
||||
add->argv.push_back("addr");
|
||||
add->argv.push_back("add");
|
||||
add->argv.push_back(local4->address + '/' + openvpn::to_string(local4->prefix_length));
|
||||
add->argv.push_back("broadcast");
|
||||
add->argv.push_back((IPv4::Addr::from_string(local4->address) | ~IPv4::Addr::netmask_from_prefix_len(local4->prefix_length)).to_string());
|
||||
add->argv.push_back("dev");
|
||||
add->argv.push_back(iface_name);
|
||||
if (unit >= 0)
|
||||
{
|
||||
add->argv.push_back("label");
|
||||
add->argv.push_back(iface_name + ':' + openvpn::to_string(unit));
|
||||
}
|
||||
create.add(add);
|
||||
|
||||
// for the destroy command, copy the add command but replace "add" with "delete"
|
||||
Command::Ptr del(add->copy());
|
||||
del->argv[3] = "del";
|
||||
destroy.add(del);
|
||||
|
||||
// add interface route to rtvec if defined
|
||||
add_del_route(local4->address, local4->prefix_length, local4->address, iface_name, R_ADD_DCO, rtvec, create, destroy);
|
||||
}
|
||||
|
||||
// Set IPv6 Interface
|
||||
if (local6 && !pull.block_ipv6)
|
||||
{
|
||||
Command::Ptr add(new Command);
|
||||
add->argv.push_back("/sbin/ip");
|
||||
add->argv.push_back("-6");
|
||||
add->argv.push_back("addr");
|
||||
add->argv.push_back("add");
|
||||
add->argv.push_back(local6->address + '/' + openvpn::to_string(local6->prefix_length));
|
||||
add->argv.push_back("dev");
|
||||
add->argv.push_back(iface_name);
|
||||
create.add(add);
|
||||
|
||||
// for the destroy command, copy the add command but replace "add" with "delete"
|
||||
Command::Ptr del(add->copy());
|
||||
del->argv[3] = "del";
|
||||
destroy.add(del);
|
||||
|
||||
// add interface route to rtvec if defined
|
||||
add_del_route(local6->address, local6->prefix_length, local6->address, iface_name, R_ADD_DCO|R_IPv6, rtvec, create, destroy);
|
||||
}
|
||||
}
|
||||
|
||||
struct TunMethods
|
||||
{
|
||||
static inline void tun_config(const std::string& iface_name,
|
||||
const TunBuilderCapture& pull,
|
||||
std::vector<IP::Route>* rtvec,
|
||||
ActionList& create,
|
||||
ActionList& destroy,
|
||||
bool add_bypass_routes = true)
|
||||
{
|
||||
const LinuxGW46 gw(true);
|
||||
|
||||
// set local4 and local6 to point to IPv4/6 route configurations
|
||||
const TunBuilderCapture::RouteAddress* local4 = pull.vpn_ipv4();
|
||||
const TunBuilderCapture::RouteAddress* local6 = pull.vpn_ipv6();
|
||||
|
||||
// configure interface
|
||||
iface_up(iface_name, pull.mtu, create, destroy);
|
||||
iface_config(iface_name, -1, pull, rtvec, create, destroy);
|
||||
|
||||
// Process Routes
|
||||
{
|
||||
for (const auto &route : pull.add_routes)
|
||||
{
|
||||
if (route.ipv6)
|
||||
{
|
||||
if (!pull.block_ipv6)
|
||||
add_del_route(route.address, route.prefix_length, local6->gateway, iface_name, R_ADD_ALL|R_IPv6, rtvec, create, destroy);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (local4 && !local4->gateway.empty())
|
||||
add_del_route(route.address, route.prefix_length, local4->gateway, iface_name, R_ADD_ALL, rtvec, create, destroy);
|
||||
else
|
||||
OPENVPN_LOG("ERROR: IPv4 route pushed without IPv4 ifconfig and/or route-gateway");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process exclude routes
|
||||
{
|
||||
for (const auto &route : pull.exclude_routes)
|
||||
{
|
||||
if (route.ipv6)
|
||||
{
|
||||
OPENVPN_LOG("NOTE: exclude IPv6 routes not supported yet"); // fixme
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gw.v4.defined())
|
||||
add_del_route(route.address, route.prefix_length, gw.v4.addr().to_string(), gw.v4.dev(), R_ADD_SYS, rtvec, create, destroy);
|
||||
else
|
||||
OPENVPN_LOG("NOTE: cannot determine gateway for exclude IPv4 routes");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process IPv4 redirect-gateway
|
||||
if (pull.reroute_gw.ipv4)
|
||||
{
|
||||
// add bypass route
|
||||
if (add_bypass_routes && !pull.remote_address.ipv6 && !(pull.reroute_gw.flags & RedirectGatewayFlags::RG_LOCAL) && gw.v4.defined())
|
||||
add_del_route(pull.remote_address.address, 32, gw.v4.addr().to_string(), gw.v4.dev(), R_ADD_SYS, rtvec, create, destroy);
|
||||
|
||||
add_del_route("0.0.0.0", 1, local4->gateway, iface_name, R_ADD_ALL, rtvec, create, destroy);
|
||||
add_del_route("128.0.0.0", 1, local4->gateway, iface_name, R_ADD_ALL, rtvec, create, destroy);
|
||||
}
|
||||
|
||||
// Process IPv6 redirect-gateway
|
||||
if (pull.reroute_gw.ipv6 && !pull.block_ipv6)
|
||||
{
|
||||
// add bypass route
|
||||
if (add_bypass_routes && pull.remote_address.ipv6 && !(pull.reroute_gw.flags & RedirectGatewayFlags::RG_LOCAL) && gw.v4.defined())
|
||||
add_del_route(pull.remote_address.address, 128, gw.v6.addr().to_string(), gw.v6.dev(), R_ADD_SYS|R_IPv6, rtvec, create, destroy);
|
||||
|
||||
add_del_route("0000::", 1, local6->gateway, iface_name, R_ADD_ALL|R_IPv6, rtvec, create, destroy);
|
||||
add_del_route("8000::", 1, local6->gateway, iface_name, R_ADD_ALL|R_IPv6, rtvec, create, destroy);
|
||||
}
|
||||
|
||||
// fixme -- Process block-ipv6
|
||||
|
||||
// fixme -- Handle pushed DNS servers
|
||||
}
|
||||
|
||||
static inline void add_bypass_route(const std::string& tun_iface_name,
|
||||
const std::string& address,
|
||||
bool ipv6,
|
||||
std::vector<IP::Route>* rtvec,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
{
|
||||
LinuxGW46 gw(true);
|
||||
|
||||
if (!ipv6 && gw.v4.defined())
|
||||
add_del_route(address, 32, gw.v4.addr().to_string(), gw.dev(), R_ADD_SYS, rtvec, create, destroy);
|
||||
|
||||
if (ipv6 && gw.v6.defined())
|
||||
add_del_route(address, 128, gw.v6.addr().to_string(), gw.dev(), R_ADD_SYS, rtvec, create, destroy);
|
||||
}
|
||||
};
|
||||
}
|
||||
} // namespace openvpn
|
||||
|
||||
@@ -27,22 +27,18 @@
|
||||
#include <net/if.h>
|
||||
#include <linux/if_tun.h>
|
||||
|
||||
#include <openvpn/asio/asioerr.hpp>
|
||||
#include <openvpn/netconf/linux/gwnetlink.hpp>
|
||||
#include <openvpn/common/action.hpp>
|
||||
#include <openvpn/tun/linux/client/sitnl.hpp>
|
||||
#include <openvpn/tun/builder/setup.hpp>
|
||||
#include <openvpn/tun/client/tunbase.hpp>
|
||||
#include <openvpn/tun/linux/client/sitnl.hpp>
|
||||
#include <openvpn/tun/linux/client/tunsetup.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace TunNetlink {
|
||||
|
||||
OPENVPN_EXCEPTION(tun_linux_error);
|
||||
OPENVPN_EXCEPTION(tun_open_error);
|
||||
OPENVPN_EXCEPTION(tun_layer_error);
|
||||
OPENVPN_EXCEPTION(tun_ioctl_error);
|
||||
OPENVPN_EXCEPTION(tun_fcntl_error);
|
||||
OPENVPN_EXCEPTION(tun_name_error);
|
||||
OPENVPN_EXCEPTION(tun_tx_queue_len_error);
|
||||
OPENVPN_EXCEPTION(tun_ifconfig_error);
|
||||
using namespace openvpn::TunLinuxSetup;
|
||||
|
||||
struct NetlinkLinkSet : public Action
|
||||
{
|
||||
@@ -600,222 +596,105 @@ namespace openvpn {
|
||||
}
|
||||
}
|
||||
|
||||
inline void tun_config(const std::string& iface_name,
|
||||
const TunBuilderCapture& pull,
|
||||
std::vector<IP::Route>* rtvec,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
struct TunMethods
|
||||
{
|
||||
const LinuxGW46Netlink gw;
|
||||
|
||||
// set local4 and local6 to point to IPv4/6 route configurations
|
||||
const TunBuilderCapture::RouteAddress* local4 = pull.vpn_ipv4();
|
||||
const TunBuilderCapture::RouteAddress* local6 = pull.vpn_ipv6();
|
||||
|
||||
// configure interface
|
||||
iface_up(iface_name, pull.mtu, create, destroy);
|
||||
iface_config(iface_name, -1, pull, rtvec, create, destroy);
|
||||
|
||||
// Process Routes
|
||||
static inline void tun_config(const std::string& iface_name,
|
||||
const TunBuilderCapture& pull,
|
||||
std::vector<IP::Route>* rtvec,
|
||||
ActionList& create,
|
||||
ActionList& destroy,
|
||||
bool add_bypass_routes)
|
||||
{
|
||||
for (const auto &route : pull.add_routes)
|
||||
{
|
||||
if (route.ipv6)
|
||||
{
|
||||
if (!pull.block_ipv6)
|
||||
add_del_route(route.address, route.prefix_length, local6->gateway, iface_name, R_ADD_ALL|R_IPv6, rtvec, create, destroy);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (local4 && !local4->gateway.empty())
|
||||
add_del_route(route.address, route.prefix_length, local4->gateway, iface_name, R_ADD_ALL, rtvec, create, destroy);
|
||||
else
|
||||
OPENVPN_LOG("ERROR: IPv4 route pushed without IPv4 ifconfig and/or route-gateway");
|
||||
}
|
||||
}
|
||||
}
|
||||
// set local4 and local6 to point to IPv4/6 route configurations
|
||||
const TunBuilderCapture::RouteAddress* local4 = pull.vpn_ipv4();
|
||||
const TunBuilderCapture::RouteAddress* local6 = pull.vpn_ipv6();
|
||||
|
||||
// Process exclude routes
|
||||
{
|
||||
for (const auto &route : pull.exclude_routes)
|
||||
{
|
||||
if (route.ipv6)
|
||||
{
|
||||
OPENVPN_LOG("NOTE: exclude IPv6 routes not supported yet"); // fixme
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gw.v4.defined())
|
||||
add_del_route(route.address, route.prefix_length, gw.v4.addr().to_string(), gw.v4.dev(), R_ADD_SYS, rtvec, create, destroy);
|
||||
else
|
||||
OPENVPN_LOG("NOTE: cannot determine gateway for exclude IPv4 routes");
|
||||
}
|
||||
}
|
||||
}
|
||||
// configure interface
|
||||
iface_up(iface_name, pull.mtu, create, destroy);
|
||||
iface_config(iface_name, -1, pull, rtvec, create, destroy);
|
||||
|
||||
// Process IPv4 redirect-gateway
|
||||
if (pull.reroute_gw.ipv4)
|
||||
// Process Routes
|
||||
{
|
||||
// add bypass route
|
||||
if (!pull.remote_address.ipv6 && !(pull.reroute_gw.flags & RedirectGatewayFlags::RG_LOCAL))
|
||||
add_del_route(pull.remote_address.address, 32, gw.v4.addr().to_string(), gw.v4.dev(), R_ADD_SYS, rtvec, create, destroy);
|
||||
|
||||
add_del_route("0.0.0.0", 1, local4->gateway, iface_name, R_ADD_ALL, rtvec, create, destroy);
|
||||
add_del_route("128.0.0.0", 1, local4->gateway, iface_name, R_ADD_ALL, rtvec, create, destroy);
|
||||
for (const auto &route : pull.add_routes)
|
||||
{
|
||||
if (route.ipv6)
|
||||
{
|
||||
if (!pull.block_ipv6)
|
||||
add_del_route(route.address, route.prefix_length, local6->gateway, iface_name, R_ADD_ALL|R_IPv6, rtvec, create, destroy);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (local4 && !local4->gateway.empty())
|
||||
add_del_route(route.address, route.prefix_length, local4->gateway, iface_name, R_ADD_ALL, rtvec, create, destroy);
|
||||
else
|
||||
OPENVPN_LOG("ERROR: IPv4 route pushed without IPv4 ifconfig and/or route-gateway");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process IPv6 redirect-gateway
|
||||
if (pull.reroute_gw.ipv6 && !pull.block_ipv6)
|
||||
// Process exclude routes
|
||||
if (!pull.exclude_routes.empty())
|
||||
{
|
||||
// add bypass route
|
||||
if (pull.remote_address.ipv6 && !(pull.reroute_gw.flags & RedirectGatewayFlags::RG_LOCAL))
|
||||
add_del_route(pull.remote_address.address, 128, gw.v6.addr().to_string(), gw.v6.dev(), R_ADD_SYS|R_IPv6, rtvec, create, destroy);
|
||||
LinuxGW46Netlink gw(iface_name);
|
||||
|
||||
add_del_route("0000::", 1, local6->gateway, iface_name, R_ADD_ALL|R_IPv6, rtvec, create, destroy);
|
||||
add_del_route("8000::", 1, local6->gateway, iface_name, R_ADD_ALL|R_IPv6, rtvec, create, destroy);
|
||||
for (const auto &route : pull.exclude_routes)
|
||||
{
|
||||
if (route.ipv6)
|
||||
{
|
||||
OPENVPN_LOG("NOTE: exclude IPv6 routes not supported yet"); // fixme
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gw.v4.defined())
|
||||
add_del_route(route.address, route.prefix_length, gw.v4.addr().to_string(), gw.v4.dev(), R_ADD_SYS, rtvec, create, destroy);
|
||||
else
|
||||
OPENVPN_LOG("NOTE: cannot determine gateway for exclude IPv4 routes");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fixme -- Process block-ipv6
|
||||
|
||||
// fixme -- Handle pushed DNS servers
|
||||
}
|
||||
|
||||
class Setup : public TunBuilderSetup::Base
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<Setup> Ptr;
|
||||
|
||||
struct Config : public TunBuilderSetup::Config
|
||||
{
|
||||
std::string iface_name;
|
||||
Layer layer; // OSI layer
|
||||
std::string dev_name;
|
||||
int txqueuelen;
|
||||
|
||||
#ifdef HAVE_JSON
|
||||
virtual Json::Value to_json() override
|
||||
{
|
||||
Json::Value root(Json::objectValue);
|
||||
root["iface_name"] = Json::Value(iface_name);
|
||||
root["layer"] = Json::Value(layer.str());
|
||||
root["dev_name"] = Json::Value(dev_name);
|
||||
root["txqueuelen"] = Json::Value(txqueuelen);
|
||||
return root;
|
||||
};
|
||||
|
||||
virtual void from_json(const Json::Value& root, const std::string& title) override
|
||||
{
|
||||
json::assert_dict(root, title);
|
||||
json::to_string(root, iface_name, "iface_name", title);
|
||||
layer = Layer::from_str(json::get_string(root, "layer", title));
|
||||
json::to_string(root, dev_name, "dev_name", title);
|
||||
json::to_int(root, txqueuelen, "txqueuelen", title);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
virtual void destroy(std::ostream &os) override
|
||||
{
|
||||
// remove added routes
|
||||
if (remove_cmds)
|
||||
remove_cmds->execute(std::cout);
|
||||
}
|
||||
|
||||
virtual int establish(const TunBuilderCapture& pull, // defined by TunBuilderSetup::Base
|
||||
TunBuilderSetup::Config* config,
|
||||
Stop* stop,
|
||||
std::ostream& os) override
|
||||
{
|
||||
// get configuration
|
||||
Config *conf = dynamic_cast<Config *>(config);
|
||||
if (!conf)
|
||||
throw tun_linux_error("missing config");
|
||||
|
||||
static const char node[] = "/dev/net/tun";
|
||||
ScopedFD fd(open(node, O_RDWR));
|
||||
if (!fd.defined())
|
||||
OPENVPN_THROW(tun_open_error, "error opening tun device " << node << ": " << errinfo(errno));
|
||||
|
||||
struct ifreq ifr;
|
||||
std::memset(&ifr, 0, sizeof(ifr));
|
||||
ifr.ifr_flags = IFF_ONE_QUEUE;
|
||||
ifr.ifr_flags |= IFF_NO_PI;
|
||||
if (conf->layer() == Layer::OSI_LAYER_3)
|
||||
ifr.ifr_flags |= IFF_TUN;
|
||||
else if (conf->layer() == Layer::OSI_LAYER_2)
|
||||
ifr.ifr_flags |= IFF_TAP;
|
||||
else
|
||||
throw tun_layer_error("unknown OSI layer");
|
||||
|
||||
open_unit(conf->dev_name, ifr, fd);
|
||||
|
||||
if (fcntl (fd(), F_SETFL, O_NONBLOCK) < 0)
|
||||
throw tun_fcntl_error(errinfo(errno));
|
||||
|
||||
// Set the TX send queue size
|
||||
if (conf->txqueuelen)
|
||||
// Process IPv4 redirect-gateway
|
||||
if (pull.reroute_gw.ipv4)
|
||||
{
|
||||
struct ifreq netifr;
|
||||
ScopedFD ctl_fd(socket (AF_INET, SOCK_DGRAM, 0));
|
||||
// add bypass route
|
||||
if (add_bypass_routes && !pull.remote_address.ipv6 && !(pull.reroute_gw.flags & RedirectGatewayFlags::RG_LOCAL))
|
||||
add_bypass_route(iface_name, pull.remote_address.address, false, rtvec, create, destroy);
|
||||
|
||||
if (ctl_fd.defined())
|
||||
{
|
||||
std::memset(&netifr, 0, sizeof(netifr));
|
||||
strcpy (netifr.ifr_name, ifr.ifr_name);
|
||||
netifr.ifr_qlen = conf->txqueuelen;
|
||||
if (ioctl (ctl_fd(), SIOCSIFTXQLEN, (void *) &netifr) < 0)
|
||||
throw tun_tx_queue_len_error(errinfo(errno));
|
||||
}
|
||||
else
|
||||
throw tun_tx_queue_len_error(errinfo(errno));
|
||||
add_del_route("0.0.0.0", 1, local4->gateway, iface_name, R_ADD_ALL, rtvec, create, destroy);
|
||||
add_del_route("128.0.0.0", 1, local4->gateway, iface_name, R_ADD_ALL, rtvec, create, destroy);
|
||||
}
|
||||
|
||||
conf->iface_name = ifr.ifr_name;
|
||||
// Process IPv6 redirect-gateway
|
||||
if (pull.reroute_gw.ipv6 && !pull.block_ipv6)
|
||||
{
|
||||
// add bypass route
|
||||
if (add_bypass_routes && pull.remote_address.ipv6 && !(pull.reroute_gw.flags & RedirectGatewayFlags::RG_LOCAL))
|
||||
add_bypass_route(iface_name, pull.remote_address.address, true, rtvec, create, destroy);
|
||||
|
||||
ActionList::Ptr add_cmds = new ActionList();
|
||||
remove_cmds.reset(new ActionListReversed()); // remove commands executed in reversed order
|
||||
add_del_route("0000::", 1, local6->gateway, iface_name, R_ADD_ALL|R_IPv6, rtvec, create, destroy);
|
||||
add_del_route("8000::", 1, local6->gateway, iface_name, R_ADD_ALL|R_IPv6, rtvec, create, destroy);
|
||||
}
|
||||
|
||||
// configure tun properties
|
||||
tun_config(ifr.ifr_name, pull, nullptr, *add_cmds, *remove_cmds);
|
||||
// fixme -- Process block-ipv6
|
||||
|
||||
// execute commands to bring up interface
|
||||
add_cmds->execute(std::cout);
|
||||
|
||||
return fd.release();
|
||||
// fixme -- Handle pushed DNS servers
|
||||
}
|
||||
|
||||
private:
|
||||
void open_unit(const std::string& name, struct ifreq& ifr, ScopedFD& fd)
|
||||
static inline void add_bypass_route(const std::string& tun_iface_name,
|
||||
const std::string& address,
|
||||
bool ipv6,
|
||||
std::vector<IP::Route>* rtvec,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
{
|
||||
if (!name.empty())
|
||||
{
|
||||
const int max_units = 256;
|
||||
for (int unit = 0; unit < max_units; ++unit)
|
||||
{
|
||||
std::string n = name;
|
||||
if (unit)
|
||||
n += openvpn::to_string(unit);
|
||||
if (n.length() < IFNAMSIZ)
|
||||
::strcpy (ifr.ifr_name, n.c_str());
|
||||
else
|
||||
throw tun_name_error();
|
||||
if (ioctl (fd(), TUNSETIFF, (void *) &ifr) == 0)
|
||||
return;
|
||||
}
|
||||
const int eno = errno;
|
||||
OPENVPN_THROW(tun_ioctl_error, "failed to open tun device '" << name << "' after trying " << max_units << " units : " << errinfo(eno));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ioctl (fd(), TUNSETIFF, (void *) &ifr) < 0)
|
||||
{
|
||||
const int eno = errno;
|
||||
OPENVPN_THROW(tun_ioctl_error, "failed to open tun device '" << name << "' : " << errinfo(eno));
|
||||
}
|
||||
}
|
||||
}
|
||||
LinuxGW46Netlink gw(tun_iface_name, address);
|
||||
|
||||
ActionListReversed::Ptr remove_cmds;
|
||||
if (!ipv6 && gw.v4.defined())
|
||||
add_del_route(address, 32, gw.v4.addr().to_string(), gw.dev(), R_ADD_SYS, rtvec, create, destroy);
|
||||
|
||||
if (ipv6 && gw.v6.defined())
|
||||
add_del_route(address, 128, gw.v6.addr().to_string(), gw.dev(), R_ADD_SYS, rtvec, create, destroy);
|
||||
}
|
||||
};
|
||||
}
|
||||
} // namespace openvpn
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include <openvpn/common/process.hpp>
|
||||
#include <openvpn/common/action.hpp>
|
||||
#include <openvpn/addr/route.hpp>
|
||||
#include <openvpn/asio/asioerr.hpp>
|
||||
#include <openvpn/tun/builder/capture.hpp>
|
||||
#include <openvpn/tun/builder/setup.hpp>
|
||||
#include <openvpn/tun/client/tunbase.hpp>
|
||||
@@ -46,7 +47,7 @@
|
||||
#include <openvpn/netconf/linux/gw.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace TunLinux {
|
||||
namespace TunLinuxSetup {
|
||||
|
||||
OPENVPN_EXCEPTION(tun_linux_error);
|
||||
OPENVPN_EXCEPTION(tun_open_error);
|
||||
@@ -57,290 +58,26 @@ namespace openvpn {
|
||||
OPENVPN_EXCEPTION(tun_tx_queue_len_error);
|
||||
OPENVPN_EXCEPTION(tun_ifconfig_error);
|
||||
|
||||
enum { // add_del_route flags
|
||||
R_IPv6=(1<<0),
|
||||
R_ADD_SYS=(1<<1),
|
||||
R_ADD_DCO=(1<<2),
|
||||
R_ADD_ALL=R_ADD_SYS|R_ADD_DCO,
|
||||
};
|
||||
|
||||
inline IP::Addr cvt_pnr_ip_v4(const std::string& hexaddr)
|
||||
{
|
||||
BufferAllocated v(4, BufferAllocated::CONSTRUCT_ZERO);
|
||||
parse_hex(v, hexaddr);
|
||||
if (v.size() != 4)
|
||||
throw tun_linux_error("bad hex address");
|
||||
IPv4::Addr ret = IPv4::Addr::from_bytes(v.data());
|
||||
return IP::Addr::from_ipv4(ret);
|
||||
}
|
||||
|
||||
inline void add_del_route(const std::string& addr_str,
|
||||
const int prefix_len,
|
||||
const std::string& gateway_str,
|
||||
const std::string& dev,
|
||||
const unsigned int flags,
|
||||
std::vector<IP::Route>* rtvec,
|
||||
Action::Ptr& create,
|
||||
Action::Ptr& destroy)
|
||||
{
|
||||
if (flags & R_IPv6)
|
||||
{
|
||||
const IPv6::Addr addr = IPv6::Addr::from_string(addr_str);
|
||||
const IPv6::Addr netmask = IPv6::Addr::netmask_from_prefix_len(prefix_len);
|
||||
const IPv6::Addr net = addr & netmask;
|
||||
|
||||
if (flags & R_ADD_SYS)
|
||||
{
|
||||
// ip route add 2001:db8:1::/48 via 2001:db8:1::1
|
||||
Command::Ptr add(new Command);
|
||||
add->argv.push_back("/sbin/ip");
|
||||
add->argv.push_back("-6");
|
||||
add->argv.push_back("route");
|
||||
add->argv.push_back("add");
|
||||
add->argv.push_back(net.to_string() + '/' + openvpn::to_string(prefix_len));
|
||||
add->argv.push_back("via");
|
||||
add->argv.push_back(gateway_str);
|
||||
if (!dev.empty())
|
||||
{
|
||||
add->argv.push_back("dev");
|
||||
add->argv.push_back(dev);
|
||||
}
|
||||
create = add;
|
||||
|
||||
// for the destroy command, copy the add command but replace "add" with "delete"
|
||||
Command::Ptr del(add->copy());
|
||||
del->argv[3] = "del";
|
||||
destroy = del;
|
||||
}
|
||||
|
||||
if (rtvec && (flags & R_ADD_DCO))
|
||||
rtvec->emplace_back(IP::Addr::from_ipv6(net), prefix_len);
|
||||
}
|
||||
else
|
||||
{
|
||||
const IPv4::Addr addr = IPv4::Addr::from_string(addr_str);
|
||||
const IPv4::Addr netmask = IPv4::Addr::netmask_from_prefix_len(prefix_len);
|
||||
const IPv4::Addr net = addr & netmask;
|
||||
|
||||
if (flags & R_ADD_SYS)
|
||||
{
|
||||
// ip route add 192.0.2.128/25 via 192.0.2.1
|
||||
Command::Ptr add(new Command);
|
||||
add->argv.push_back("/sbin/ip");
|
||||
add->argv.push_back("-4");
|
||||
add->argv.push_back("route");
|
||||
add->argv.push_back("add");
|
||||
add->argv.push_back(net.to_string() + '/' + openvpn::to_string(prefix_len));
|
||||
add->argv.push_back("via");
|
||||
add->argv.push_back(gateway_str);
|
||||
create = add;
|
||||
|
||||
// for the destroy command, copy the add command but replace "add" with "delete"
|
||||
Command::Ptr del(add->copy());
|
||||
del->argv[3] = "del";
|
||||
destroy = del;
|
||||
}
|
||||
|
||||
if (rtvec && (flags & R_ADD_DCO))
|
||||
rtvec->emplace_back(IP::Addr::from_ipv4(net), prefix_len);
|
||||
}
|
||||
}
|
||||
|
||||
inline void add_del_route(const std::string& addr_str,
|
||||
const int prefix_len,
|
||||
const std::string& gateway_str,
|
||||
const std::string& dev,
|
||||
const unsigned int flags,// add interface route to rtvec if defined
|
||||
std::vector<IP::Route>* rtvec,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
{
|
||||
Action::Ptr c, d;
|
||||
add_del_route(addr_str, prefix_len, gateway_str, dev, flags, rtvec, c, d);
|
||||
create.add(c);
|
||||
destroy.add(d);
|
||||
}
|
||||
|
||||
inline void iface_up(const std::string& iface_name,
|
||||
const int mtu,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
{
|
||||
{
|
||||
Command::Ptr add(new Command);
|
||||
add->argv.push_back("/sbin/ip");
|
||||
add->argv.push_back("link");
|
||||
add->argv.push_back("set");
|
||||
add->argv.push_back(iface_name);
|
||||
add->argv.push_back("up");
|
||||
if (mtu > 0)
|
||||
{
|
||||
add->argv.push_back("mtu");
|
||||
add->argv.push_back(openvpn::to_string(mtu));
|
||||
}
|
||||
create.add(add);
|
||||
|
||||
// for the destroy command, copy the add command but replace "up" with "down"
|
||||
Command::Ptr del(add->copy());
|
||||
del->argv[4] = "down";
|
||||
destroy.add(del);
|
||||
}
|
||||
}
|
||||
|
||||
inline void iface_config(const std::string& iface_name,
|
||||
int unit,
|
||||
const TunBuilderCapture& pull,
|
||||
std::vector<IP::Route>* rtvec,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
{
|
||||
// set local4 and local6 to point to IPv4/6 route configurations
|
||||
const TunBuilderCapture::RouteAddress* local4 = pull.vpn_ipv4();
|
||||
const TunBuilderCapture::RouteAddress* local6 = pull.vpn_ipv6();
|
||||
|
||||
// Set IPv4 Interface
|
||||
if (local4)
|
||||
{
|
||||
Command::Ptr add(new Command);
|
||||
add->argv.push_back("/sbin/ip");
|
||||
add->argv.push_back("-4");
|
||||
add->argv.push_back("addr");
|
||||
add->argv.push_back("add");
|
||||
add->argv.push_back(local4->address + '/' + openvpn::to_string(local4->prefix_length));
|
||||
add->argv.push_back("broadcast");
|
||||
add->argv.push_back((IPv4::Addr::from_string(local4->address) | ~IPv4::Addr::netmask_from_prefix_len(local4->prefix_length)).to_string());
|
||||
add->argv.push_back("dev");
|
||||
add->argv.push_back(iface_name);
|
||||
if (unit >= 0)
|
||||
{
|
||||
add->argv.push_back("label");
|
||||
add->argv.push_back(iface_name + ':' + openvpn::to_string(unit));
|
||||
}
|
||||
create.add(add);
|
||||
|
||||
// for the destroy command, copy the add command but replace "add" with "delete"
|
||||
Command::Ptr del(add->copy());
|
||||
del->argv[3] = "del";
|
||||
destroy.add(del);
|
||||
|
||||
// add interface route to rtvec if defined
|
||||
add_del_route(local4->address, local4->prefix_length, local4->address, iface_name, R_ADD_DCO, rtvec, create, destroy);
|
||||
}
|
||||
|
||||
// Set IPv6 Interface
|
||||
if (local6 && !pull.block_ipv6)
|
||||
{
|
||||
Command::Ptr add(new Command);
|
||||
add->argv.push_back("/sbin/ip");
|
||||
add->argv.push_back("-6");
|
||||
add->argv.push_back("addr");
|
||||
add->argv.push_back("add");
|
||||
add->argv.push_back(local6->address + '/' + openvpn::to_string(local6->prefix_length));
|
||||
add->argv.push_back("dev");
|
||||
add->argv.push_back(iface_name);
|
||||
create.add(add);
|
||||
|
||||
// for the destroy command, copy the add command but replace "add" with "delete"
|
||||
Command::Ptr del(add->copy());
|
||||
del->argv[3] = "del";
|
||||
destroy.add(del);
|
||||
|
||||
// add interface route to rtvec if defined
|
||||
add_del_route(local6->address, local6->prefix_length, local6->address, iface_name, R_ADD_DCO|R_IPv6, rtvec, create, destroy);
|
||||
}
|
||||
}
|
||||
|
||||
inline void tun_config(const std::string& iface_name,
|
||||
const TunBuilderCapture& pull,
|
||||
std::vector<IP::Route>* rtvec,
|
||||
ActionList& create,
|
||||
ActionList& destroy)
|
||||
{
|
||||
const LinuxGW46 gw(true);
|
||||
|
||||
// set local4 and local6 to point to IPv4/6 route configurations
|
||||
const TunBuilderCapture::RouteAddress* local4 = pull.vpn_ipv4();
|
||||
const TunBuilderCapture::RouteAddress* local6 = pull.vpn_ipv6();
|
||||
|
||||
// configure interface
|
||||
iface_up(iface_name, pull.mtu, create, destroy);
|
||||
iface_config(iface_name, -1, pull, rtvec, create, destroy);
|
||||
|
||||
// Process Routes
|
||||
{
|
||||
for (const auto &route : pull.add_routes)
|
||||
{
|
||||
if (route.ipv6)
|
||||
{
|
||||
if (!pull.block_ipv6)
|
||||
add_del_route(route.address, route.prefix_length, local6->gateway, iface_name, R_ADD_ALL|R_IPv6, rtvec, create, destroy);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (local4 && !local4->gateway.empty())
|
||||
add_del_route(route.address, route.prefix_length, local4->gateway, iface_name, R_ADD_ALL, rtvec, create, destroy);
|
||||
else
|
||||
OPENVPN_LOG("ERROR: IPv4 route pushed without IPv4 ifconfig and/or route-gateway");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process exclude routes
|
||||
{
|
||||
for (const auto &route : pull.exclude_routes)
|
||||
{
|
||||
if (route.ipv6)
|
||||
{
|
||||
OPENVPN_LOG("NOTE: exclude IPv6 routes not supported yet"); // fixme
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gw.v4.defined())
|
||||
add_del_route(route.address, route.prefix_length, gw.v4.addr().to_string(), gw.v4.dev(), R_ADD_SYS, rtvec, create, destroy);
|
||||
else
|
||||
OPENVPN_LOG("NOTE: cannot determine gateway for exclude IPv4 routes");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process IPv4 redirect-gateway
|
||||
if (pull.reroute_gw.ipv4)
|
||||
{
|
||||
// add bypass route
|
||||
if (!pull.remote_address.ipv6 && !(pull.reroute_gw.flags & RedirectGatewayFlags::RG_LOCAL))
|
||||
add_del_route(pull.remote_address.address, 32, gw.v4.addr().to_string(), gw.v4.dev(), R_ADD_SYS, rtvec, create, destroy);
|
||||
|
||||
add_del_route("0.0.0.0", 1, local4->gateway, iface_name, R_ADD_ALL, rtvec, create, destroy);
|
||||
add_del_route("128.0.0.0", 1, local4->gateway, iface_name, R_ADD_ALL, rtvec, create, destroy);
|
||||
}
|
||||
|
||||
// Process IPv6 redirect-gateway
|
||||
if (pull.reroute_gw.ipv6 && !pull.block_ipv6)
|
||||
{
|
||||
// add bypass route
|
||||
if (pull.remote_address.ipv6 && !(pull.reroute_gw.flags & RedirectGatewayFlags::RG_LOCAL))
|
||||
add_del_route(pull.remote_address.address, 128, gw.v6.addr().to_string(), gw.v6.dev(), R_ADD_SYS|R_IPv6, rtvec, create, destroy);
|
||||
|
||||
add_del_route("0000::", 1, local6->gateway, iface_name, R_ADD_ALL|R_IPv6, rtvec, create, destroy);
|
||||
add_del_route("8000::", 1, local6->gateway, iface_name, R_ADD_ALL|R_IPv6, rtvec, create, destroy);
|
||||
}
|
||||
|
||||
// fixme -- Process block-ipv6
|
||||
|
||||
// fixme -- Handle pushed DNS servers
|
||||
}
|
||||
|
||||
template <class TUNMETHODS>
|
||||
class Setup : public TunBuilderSetup::Base
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<Setup> Ptr;
|
||||
|
||||
// This empty constructor shouldn't be needed, but due to a
|
||||
// plausible compiler bug in GCC 4.8.5 (RHEL 7), this empty
|
||||
// constructor is required to be able to build. This is
|
||||
// related to the member initialization of the private
|
||||
// remove_cmds_bypass_gw and remove_cmds class members.
|
||||
Setup() {}
|
||||
|
||||
struct Config : public TunBuilderSetup::Config
|
||||
{
|
||||
std::string iface_name;
|
||||
Layer layer; // OSI layer
|
||||
std::string dev_name;
|
||||
int txqueuelen;
|
||||
bool add_bypass_routes_on_establish; // required when not using tunbuilder
|
||||
|
||||
#ifdef HAVE_JSON
|
||||
virtual Json::Value to_json() override
|
||||
@@ -364,17 +101,39 @@ namespace openvpn {
|
||||
#endif
|
||||
};
|
||||
|
||||
virtual void destroy(std::ostream &os) override
|
||||
void destroy(std::ostream &os) override
|
||||
{
|
||||
// remove added routes
|
||||
if (remove_cmds)
|
||||
remove_cmds->execute(std::cout);
|
||||
remove_cmds->execute(os);
|
||||
|
||||
// remove bypass route
|
||||
remove_cmds_bypass_gw->execute(os);
|
||||
}
|
||||
|
||||
virtual int establish(const TunBuilderCapture& pull, // defined by TunBuilderSetup::Base
|
||||
TunBuilderSetup::Config* config,
|
||||
Stop* stop,
|
||||
std::ostream& os) override
|
||||
bool add_bypass_route(const std::string& address,
|
||||
bool ipv6,
|
||||
std::ostream& os)
|
||||
{
|
||||
// nothing to do if we reconnect to the same gateway
|
||||
if (connected_gw == address)
|
||||
return true;
|
||||
|
||||
// remove previous bypass route
|
||||
remove_cmds_bypass_gw->execute(os);
|
||||
remove_cmds_bypass_gw->clear();
|
||||
|
||||
ActionList::Ptr add_cmds = new ActionList();
|
||||
TUNMETHODS::add_bypass_route(tun_iface_name, address, ipv6, nullptr, *add_cmds, *remove_cmds_bypass_gw);
|
||||
|
||||
// add gateway bypass route
|
||||
add_cmds->execute(os);
|
||||
return true;
|
||||
}
|
||||
|
||||
int establish(const TunBuilderCapture& pull, // defined by TunBuilderSetup::Base
|
||||
TunBuilderSetup::Config* config,
|
||||
Stop* stop,
|
||||
std::ostream& os) override
|
||||
{
|
||||
// get configuration
|
||||
Config *conf = dynamic_cast<Config *>(config);
|
||||
@@ -421,15 +180,22 @@ namespace openvpn {
|
||||
}
|
||||
|
||||
conf->iface_name = ifr.ifr_name;
|
||||
tun_iface_name = ifr.ifr_name;
|
||||
|
||||
ActionList::Ptr add_cmds = new ActionList();
|
||||
remove_cmds.reset(new ActionListReversed()); // remove commands executed in reversed order
|
||||
ActionList::Ptr remove_cmds_new = new ActionListReversed();
|
||||
|
||||
// configure tun properties
|
||||
tun_config(ifr.ifr_name, pull, nullptr, *add_cmds, *remove_cmds);
|
||||
TUNMETHODS::tun_config(ifr.ifr_name, pull, nullptr, *add_cmds, *remove_cmds_new, conf->add_bypass_routes_on_establish);
|
||||
|
||||
// execute commands to bring up interface
|
||||
add_cmds->execute(std::cout);
|
||||
add_cmds->execute(os);
|
||||
|
||||
// tear down old routes
|
||||
remove_cmds->execute(os);
|
||||
std::swap(remove_cmds, remove_cmds_new);
|
||||
|
||||
connected_gw = pull.remote_address.to_string();
|
||||
|
||||
return fd.release();
|
||||
}
|
||||
@@ -465,7 +231,12 @@ namespace openvpn {
|
||||
}
|
||||
}
|
||||
|
||||
ActionListReversed::Ptr remove_cmds;
|
||||
ActionList::Ptr remove_cmds_bypass_gw = new ActionList();
|
||||
ActionListReversed::Ptr remove_cmds = new ActionListReversed();
|
||||
|
||||
std::string connected_gw;
|
||||
|
||||
std::string tun_iface_name; // used to skip tun-based default gw when add bypass route
|
||||
};
|
||||
}
|
||||
} // namespace openvpn
|
||||
|
||||
@@ -64,6 +64,7 @@ namespace openvpn {
|
||||
std::string iface_name;
|
||||
Layer layer; // OSI layer
|
||||
bool tun_prefix = false;
|
||||
bool add_bypass_routes_on_establish = false;
|
||||
|
||||
#ifdef HAVE_JSON
|
||||
virtual Json::Value to_json() override
|
||||
@@ -85,6 +86,14 @@ namespace openvpn {
|
||||
#endif
|
||||
};
|
||||
|
||||
bool add_bypass_route(const std::string& address,
|
||||
bool ipv6,
|
||||
std::ostream& os)
|
||||
{
|
||||
// not yet implemented
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual int establish(const TunBuilderCapture& pull, // defined by TunBuilderSetup::Base
|
||||
TunBuilderSetup::Config* config,
|
||||
Stop* stop,
|
||||
|
||||
@@ -45,14 +45,15 @@ namespace openvpn {
|
||||
|
||||
TunIO(ReadHandler read_handler_arg,
|
||||
const Frame::Ptr& frame_arg,
|
||||
const SessionStats::Ptr& stats_arg)
|
||||
const SessionStats::Ptr& stats_arg,
|
||||
const size_t frame_context_type=Frame::READ_TUN)
|
||||
: stream(nullptr),
|
||||
retain_stream(false),
|
||||
tun_prefix(false),
|
||||
halt(false),
|
||||
read_handler(read_handler_arg),
|
||||
frame(frame_arg),
|
||||
frame_context((*frame_arg)[Frame::READ_TUN]),
|
||||
frame_context((*frame_arg)[frame_context_type]),
|
||||
stats(stats_arg)
|
||||
{
|
||||
}
|
||||
@@ -115,7 +116,8 @@ namespace openvpn {
|
||||
catch (openvpn_io::system_error& e)
|
||||
{
|
||||
OPENVPN_LOG_TUN_ERROR("TUN write exception: " << e.what());
|
||||
tun_error(Error::TUN_WRITE_ERROR, &e.code());
|
||||
const openvpn_io::error_code code(e.code());
|
||||
tun_error(Error::TUN_WRITE_ERROR, &code);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -148,7 +150,8 @@ namespace openvpn {
|
||||
catch (openvpn_io::system_error& e)
|
||||
{
|
||||
OPENVPN_LOG_TUN_ERROR("TUN write exception: " << e.what());
|
||||
tun_error(Error::TUN_WRITE_ERROR, &e.code());
|
||||
const openvpn_io::error_code code(e.code());
|
||||
tun_error(Error::TUN_WRITE_ERROR, &code);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
// packet encryption, packet authentication, and
|
||||
// packet compression.
|
||||
//
|
||||
// Copyright (C) 2012-2017 OpenVPN Inc.
|
||||
// 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
|
||||
@@ -67,7 +67,7 @@ namespace openvpn {
|
||||
{
|
||||
typedef RCPtr<SetupFactory> Ptr;
|
||||
|
||||
virtual SetupBase::Ptr new_setup_obj(openvpn_io::io_context& io_context) = 0;
|
||||
virtual SetupBase::Ptr new_setup_obj(openvpn_io::io_context& io_context, bool wintun) = 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,10 @@
|
||||
#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
|
||||
|
||||
namespace openvpn {
|
||||
namespace TunWin {
|
||||
|
||||
@@ -67,8 +71,9 @@ namespace openvpn {
|
||||
const bool retain_stream,
|
||||
ReadHandler read_handler,
|
||||
const Frame::Ptr& frame,
|
||||
const SessionStats::Ptr& stats)
|
||||
: Base(read_handler, frame, stats)
|
||||
const SessionStats::Ptr& stats,
|
||||
const bool wintun)
|
||||
: Base(read_handler, frame, stats, wintun ? Frame::READ_WINTUN : Frame::READ_TUN)
|
||||
{
|
||||
Base::name_ = name;
|
||||
Base::retain_stream = retain_stream;
|
||||
@@ -90,6 +95,7 @@ namespace openvpn {
|
||||
|
||||
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;
|
||||
@@ -103,9 +109,9 @@ namespace openvpn {
|
||||
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);
|
||||
return tun_setup_factory->new_setup_obj(io_context, wintun);
|
||||
else
|
||||
return new TunWin::Setup(io_context);
|
||||
return new TunWin::Setup(io_context, wintun);
|
||||
}
|
||||
|
||||
static Ptr new_obj()
|
||||
@@ -219,8 +225,8 @@ namespace openvpn {
|
||||
true,
|
||||
this,
|
||||
config->frame,
|
||||
config->stats
|
||||
));
|
||||
config->stats,
|
||||
config->wintun));
|
||||
impl->start(config->n_parallel);
|
||||
|
||||
if (!dhcp_capture)
|
||||
@@ -301,7 +307,8 @@ namespace openvpn {
|
||||
parent(parent_arg),
|
||||
state(new TunProp::State()),
|
||||
l2_timer(io_context_arg),
|
||||
halt(false)
|
||||
halt(false),
|
||||
frame_context((*config_arg->frame)[Frame::READ_TUN])
|
||||
{
|
||||
}
|
||||
|
||||
@@ -311,6 +318,20 @@ 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
|
||||
@@ -322,7 +343,35 @@ namespace openvpn {
|
||||
|
||||
void tun_read_handler(PacketFrom::SPtr& pfp) // called by TunImpl
|
||||
{
|
||||
parent.tun_recv(pfp->buf);
|
||||
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);
|
||||
}
|
||||
|
||||
#ifdef OPENVPN_DEBUG_TAPWIN
|
||||
tap_process_logging();
|
||||
#endif
|
||||
@@ -438,6 +487,10 @@ 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;
|
||||
};
|
||||
|
||||
|
||||
@@ -60,8 +60,9 @@ namespace openvpn {
|
||||
public:
|
||||
typedef RCPtr<Setup> Ptr;
|
||||
|
||||
Setup(openvpn_io::io_context& io_context_arg)
|
||||
: delete_route_timer(io_context_arg) {}
|
||||
Setup(openvpn_io::io_context& io_context_arg, bool wintun_arg=false)
|
||||
: delete_route_timer(io_context_arg),
|
||||
wintun(wintun_arg) {}
|
||||
|
||||
// Set up the TAP device
|
||||
virtual HANDLE establish(const TunBuilderCapture& pull,
|
||||
@@ -73,13 +74,13 @@ namespace openvpn {
|
||||
destroy(os);
|
||||
|
||||
// enumerate available TAP adapters
|
||||
Util::TapNameGuidPairList guids;
|
||||
Util::TapNameGuidPairList guids(wintun);
|
||||
os << "TAP ADAPTERS:" << std::endl << guids.to_string() << std::endl;
|
||||
|
||||
// open TAP device handle
|
||||
std::string path_opened;
|
||||
Util::TapNameGuidPair tap;
|
||||
Win::ScopedHANDLE th(Util::tap_open(guids, path_opened, tap));
|
||||
Win::ScopedHANDLE th(Util::tap_open(guids, path_opened, tap, wintun));
|
||||
const std::string msg = "Open TAP device \"" + tap.name + "\" PATH=\"" + path_opened + '\"';
|
||||
|
||||
if (!th.defined())
|
||||
@@ -89,8 +90,11 @@ namespace openvpn {
|
||||
}
|
||||
|
||||
os << msg << " SUCCEEDED" << std::endl;
|
||||
Util::TAPDriverVersion version(th());
|
||||
os << version.to_string() << std::endl;
|
||||
if (!wintun)
|
||||
{
|
||||
Util::TAPDriverVersion version(th());
|
||||
os << version.to_string() << std::endl;
|
||||
}
|
||||
|
||||
// create ActionLists for setting up and removing adapter properties
|
||||
ActionList::Ptr add_cmds(new ActionList());
|
||||
@@ -100,7 +104,7 @@ namespace openvpn {
|
||||
switch (pull.layer())
|
||||
{
|
||||
case Layer::OSI_LAYER_3:
|
||||
adapter_config(th(), openvpn_app_path, tap, pull, false, *add_cmds, *remove_cmds, os);
|
||||
adapter_config(th(), openvpn_app_path, tap, wintun, 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);
|
||||
@@ -158,7 +162,7 @@ namespace openvpn {
|
||||
{
|
||||
Win::ScopedHANDLE nh;
|
||||
ActionList::Ptr add_cmds(new ActionList());
|
||||
adapter_config(nh(), l2s->openvpn_app_path, l2s->tap, pull, true, *add_cmds, *remove_cmds, os);
|
||||
adapter_config(nh(), l2s->openvpn_app_path, l2s->tap, wintun, pull, true, *add_cmds, *remove_cmds, os);
|
||||
add_cmds->execute(os);
|
||||
}
|
||||
}
|
||||
@@ -251,6 +255,7 @@ namespace openvpn {
|
||||
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,
|
||||
@@ -273,7 +278,8 @@ namespace openvpn {
|
||||
if (!l2_post)
|
||||
{
|
||||
// set TAP media status to CONNECTED
|
||||
Util::tap_set_media_status(th, true);
|
||||
if (!wintun)
|
||||
Util::tap_set_media_status(th, true);
|
||||
|
||||
// try to delete any stale routes on interface left over from previous session
|
||||
create.add(new Util::ActionDeleteAllRoutesOnInterface(tap.index));
|
||||
@@ -302,10 +308,13 @@ namespace openvpn {
|
||||
const std::string metric = route_metric_opt(pull, *local4, MT_IFACE);
|
||||
const std::string netmask = IPv4::Addr::netmask_from_prefix_len(local4->prefix_length).to_string();
|
||||
const IP::Addr localaddr = IP::Addr::from_string(local4->address);
|
||||
if (local4->net30)
|
||||
Util::tap_configure_topology_net30(th, localaddr, local4->prefix_length);
|
||||
else
|
||||
Util::tap_configure_topology_subnet(th, localaddr, local4->prefix_length);
|
||||
if (!wintun)
|
||||
{
|
||||
if (local4->net30)
|
||||
Util::tap_configure_topology_net30(th, localaddr, local4->prefix_length);
|
||||
else
|
||||
Util::tap_configure_topology_subnet(th, localaddr, local4->prefix_length);
|
||||
}
|
||||
create.add(new WinCmd("netsh interface ip set address " + tap_index_name + " static " + local4->address + ' ' + netmask + " gateway=" + local4->gateway + metric + " store=active"));
|
||||
destroy.add(new WinCmd("netsh interface ip delete address " + tap_index_name + ' ' + local4->address + " gateway=all store=active"));
|
||||
|
||||
@@ -671,7 +680,8 @@ namespace openvpn {
|
||||
}
|
||||
|
||||
// set TAP media status to CONNECTED
|
||||
Util::tap_set_media_status(th, true);
|
||||
if (!wintun)
|
||||
Util::tap_set_media_status(th, true);
|
||||
|
||||
// ARP
|
||||
Util::flush_arp(tap.index, os);
|
||||
@@ -859,6 +869,8 @@ namespace openvpn {
|
||||
ActionList::Ptr remove_cmds;
|
||||
|
||||
AsioTimer delete_route_timer;
|
||||
|
||||
bool wintun = false;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+130
-13
@@ -33,6 +33,7 @@
|
||||
#include <ntddndis.h>
|
||||
#include <wininet.h>
|
||||
#include <ws2tcpip.h> // for IPv6
|
||||
#include <tlhelp32.h> // for impersonating as LocalSystem
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -71,17 +72,20 @@ namespace openvpn {
|
||||
|
||||
// generally defined on cl command line
|
||||
const char COMPONENT_ID[] = OPENVPN_STRINGIZE(TAP_WIN_COMPONENT_ID); // CONST GLOBAL
|
||||
const char WINTUN_COMPONENT_ID[] = "wintun"; // CONST GLOBAL
|
||||
}
|
||||
|
||||
using TapGuidLuid = std::pair<std::string, DWORD>;
|
||||
|
||||
// Return a list of TAP device GUIDs installed on the system,
|
||||
// filtered by TAP_WIN_COMPONENT_ID.
|
||||
inline std::vector<std::string> tap_guids()
|
||||
inline std::vector<TapGuidLuid> tap_guids(bool wintun)
|
||||
{
|
||||
LONG status;
|
||||
DWORD len;
|
||||
DWORD data_type;
|
||||
|
||||
std::vector<std::string> ret;
|
||||
std::vector<TapGuidLuid> ret;
|
||||
|
||||
Win::RegKey adapter_key;
|
||||
status = ::RegOpenKeyExA(HKEY_LOCAL_MACHINE,
|
||||
@@ -139,9 +143,11 @@ namespace openvpn {
|
||||
if (status != ERROR_SUCCESS || data_type != REG_SZ)
|
||||
continue;
|
||||
strbuf[len] = '\0';
|
||||
if (std::strcmp(strbuf, COMPONENT_ID))
|
||||
if (std::strcmp(strbuf, wintun ? WINTUN_COMPONENT_ID : COMPONENT_ID))
|
||||
continue;
|
||||
|
||||
TapGuidLuid tgl;
|
||||
|
||||
len = sizeof(strbuf);
|
||||
status = ::RegQueryValueExA(unit_key(),
|
||||
"NetCfgInstanceId",
|
||||
@@ -153,8 +159,24 @@ namespace openvpn {
|
||||
if (status == ERROR_SUCCESS && data_type == REG_SZ)
|
||||
{
|
||||
strbuf[len] = '\0';
|
||||
ret.push_back(std::string(strbuf));
|
||||
tgl.first = std::string(strbuf);
|
||||
}
|
||||
|
||||
DWORD luid;
|
||||
len = sizeof(luid);
|
||||
status = ::RegQueryValueExA(unit_key(),
|
||||
"NetLuidIndex",
|
||||
nullptr,
|
||||
&data_type,
|
||||
(LPBYTE)&luid,
|
||||
&len);
|
||||
|
||||
if (status == ERROR_SUCCESS && data_type == REG_DWORD)
|
||||
{
|
||||
tgl.second = luid;
|
||||
}
|
||||
|
||||
ret.push_back(tgl);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -177,20 +199,22 @@ namespace openvpn {
|
||||
|
||||
std::string name;
|
||||
std::string guid;
|
||||
DWORD net_luid_index;
|
||||
DWORD index;
|
||||
};
|
||||
|
||||
struct TapNameGuidPairList : public std::vector<TapNameGuidPair>
|
||||
{
|
||||
TapNameGuidPairList()
|
||||
TapNameGuidPairList(bool wintun)
|
||||
{
|
||||
// first get the TAP guids
|
||||
{
|
||||
std::vector<std::string> guids = tap_guids();
|
||||
for (std::vector<std::string>::const_iterator i = guids.begin(); i != guids.end(); i++)
|
||||
std::vector<TapGuidLuid> guids = tap_guids(wintun);
|
||||
for (auto i = guids.begin(); i != guids.end(); i++)
|
||||
{
|
||||
TapNameGuidPair pair;
|
||||
pair.guid = *i;
|
||||
pair.guid = i->first;
|
||||
pair.net_luid_index = i->second;
|
||||
|
||||
// lookup adapter index
|
||||
{
|
||||
@@ -318,16 +342,101 @@ namespace openvpn {
|
||||
}
|
||||
};
|
||||
|
||||
// given a TAP GUID, form the pathname of the TAP device node
|
||||
inline std::string tap_path(const std::string& tap_guid)
|
||||
inline HANDLE impersonate_as_system()
|
||||
{
|
||||
return std::string(USERMODEDEVICEDIR) + tap_guid + std::string(TAP_WIN_SUFFIX);
|
||||
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 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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
winlogon_process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
|
||||
if (!winlogon_process)
|
||||
{
|
||||
RevertToSelf();
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
if (!OpenProcessToken(winlogon_process, TOKEN_IMPERSONATE | TOKEN_DUPLICATE, &winlogon_token))
|
||||
{
|
||||
CloseHandle(winlogon_process);
|
||||
RevertToSelf();
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
CloseHandle(winlogon_process);
|
||||
|
||||
if (!DuplicateToken(winlogon_token, SecurityImpersonation, &duplicated_token))
|
||||
{
|
||||
CloseHandle(winlogon_token);
|
||||
RevertToSelf();
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
CloseHandle(winlogon_token);
|
||||
|
||||
if (!SetThreadToken(NULL, duplicated_token))
|
||||
{
|
||||
CloseHandle(duplicated_token);
|
||||
RevertToSelf();
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
CloseHandle(duplicated_token);
|
||||
}
|
||||
|
||||
// given a TAP GUID, form the pathname of the TAP device node
|
||||
inline std::string tap_path(const TapNameGuidPair& tap, bool wintun)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
// open an available TAP adapter
|
||||
inline HANDLE tap_open(const TapNameGuidPairList& guids,
|
||||
std::string& path_opened,
|
||||
TapNameGuidPair& used)
|
||||
TapNameGuidPair& used,
|
||||
bool wintun)
|
||||
{
|
||||
Win::ScopedHANDLE hand;
|
||||
|
||||
@@ -335,7 +444,12 @@ namespace openvpn {
|
||||
for (TapNameGuidPairList::const_iterator i = guids.begin(); i != guids.end(); i++)
|
||||
{
|
||||
const TapNameGuidPair& tap = *i;
|
||||
const std::string path = tap_path(tap.guid);
|
||||
const std::string path = tap_path(tap, wintun);
|
||||
|
||||
// wintun device can be only opened under LocalSystem account
|
||||
if (wintun)
|
||||
impersonate_as_system();
|
||||
|
||||
hand.reset(::CreateFileA(path.c_str(),
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
0, /* was: FILE_SHARE_READ */
|
||||
@@ -343,6 +457,9 @@ namespace openvpn {
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
|
||||
0));
|
||||
if (wintun)
|
||||
RevertToSelf();
|
||||
|
||||
if (hand.defined())
|
||||
{
|
||||
used = tap;
|
||||
|
||||
Reference in New Issue
Block a user