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:
Sergey Abramchuk
2019-06-17 09:44:01 +03:00
parent e9b9b66317
commit 5edb23a7ab
126 changed files with 42387 additions and 1287 deletions

7
CMakeLists.txt Normal file
View File

@@ -0,0 +1,7 @@
# Cmake in OpenVPN3 core is mainly used to get Clion editior support and to run unit tests. For normal
# Build instructions see the README.rst
cmake_minimum_required(VERSION 3.5)
add_subdirectory(test/unittests)
add_subdirectory(test/ovpncli)

View File

@@ -169,7 +169,7 @@ Build test client::
> c:\Temp\O3\core\win>set O3=C:\Temp\O3 && python build.py
Visual Studio 2015 project and solution files are located in ``O3\core\win`` directory.
Visual Studio 2017 project and solution files are located in ``O3\core\win`` directory.
Before opening project you need to build dependencies and define OVPN3_ROOT
environmental variable (``C:\Temp\O3`` from example above).

24
appveyor.yml Normal file
View File

@@ -0,0 +1,24 @@
version: 1.0.{build}
image: Visual Studio 2017
clone_folder: c:\ovpn3\core
install:
- pip install rfc6266 requests
environment:
MSVC_DIR: C:\Program Files (x86)\Microsoft Visual Studio\2017\Community
O3: C:\ovpn3
STATIC: 1
before_build:
- cmd: cd win && python buildep.py
platform: x64
configuration: Release
artifacts:
- path: win\x64\Release\cli.exe

View File

@@ -422,6 +422,7 @@ namespace openvpn {
IPv6Setting ipv6;
int conn_timeout = 0;
bool tun_persist = false;
bool wintun = false;
bool google_dns_fallback = false;
bool synchronous_dns_lookup = false;
bool autologin_sessions = false;
@@ -435,6 +436,7 @@ namespace openvpn {
std::string tls_version_min_override;
std::string tls_cert_profile_override;
std::string gui_version;
bool allow_local_lan_access;
ProtoContextOptions::Ptr proto_context_options;
PeerInfo::Set::Ptr extra_peer_info;
HTTPProxyTransport::Options::Ptr http_proxy_options;
@@ -665,6 +667,7 @@ namespace openvpn {
state->port_override = config.portOverride;
state->conn_timeout = config.connTimeout;
state->tun_persist = config.tunPersist;
state->wintun = config.wintun;
state->google_dns_fallback = config.googleDnsFallback;
state->synchronous_dns_lookup = config.synchronousDnsLookup;
state->autologin_sessions = config.autologinSessions;
@@ -684,6 +687,7 @@ namespace openvpn {
state->force_aes_cbc_ciphersuites = config.forceAesCbcCiphersuites;
state->tls_version_min_override = config.tlsVersionMinOverride;
state->tls_cert_profile_override = config.tlsCertProfileOverride;
state->allow_local_lan_access = config.allowLocalLanAccess;
state->gui_version = config.guiVersion;
state->alt_proxy = config.altProxy;
state->dco = config.dco;
@@ -936,6 +940,7 @@ namespace openvpn {
cc.ipv6 = state->ipv6;
cc.conn_timeout = state->conn_timeout;
cc.tun_persist = state->tun_persist;
cc.wintun = state->wintun;
cc.google_dns_fallback = state->google_dns_fallback;
cc.synchronous_dns_lookup = state->synchronous_dns_lookup;
cc.autologin_sessions = state->autologin_sessions;
@@ -959,6 +964,7 @@ namespace openvpn {
cc.gui_version = state->gui_version;
cc.extra_peer_info = state->extra_peer_info;
cc.stop = state->async_stop_local();
cc.allow_local_lan_access = state->allow_local_lan_access;
#ifdef OPENVPN_GREMLIN
cc.gremlin_config = state->gremlin_config;
#endif
@@ -1140,11 +1146,12 @@ namespace openvpn {
}
}
OPENVPN_CLIENT_EXPORT bool OpenVPNClient::sign(const std::string& data, std::string& sig)
OPENVPN_CLIENT_EXPORT bool OpenVPNClient::sign(const std::string& data, std::string& sig, const std::string& algorithm)
{
ExternalPKISignRequest req;
req.data = data;
req.alias = state->external_pki_alias;
req.algorithm = algorithm;
external_pki_sign_request(req); // call out to derived class for RSA signature
if (!req.error)
{
@@ -1377,7 +1384,9 @@ namespace openvpn {
#ifdef OPENVPN_GREMLIN
ret += " GREMLIN";
#endif
#ifdef OPENVPN_DEBUG
ret += " built on " __DATE__ " " __TIME__;
#endif
return ret;
}

View File

@@ -282,6 +282,10 @@ namespace openvpn {
// pass through control channel INFO notifications via "INFO" event
bool info = false;
// Allow access to local LAN. This is for platforms like
// Android that disable local LAN access by default.
bool allowLocalLanAccess = false;
// Periodic convenience clock tick in milliseconds.
// Will call clock_tick() at a frequency defined by this parameter.
// Set to 0 to disable.
@@ -289,6 +293,9 @@ namespace openvpn {
// Gremlin configuration (requires that the core is built with OPENVPN_GREMLIN)
std::string gremlinConfig;
// Use wintun instead of tap-windows6 on Windows
bool wintun = false;
};
// used to communicate VPN events such as connect, disconnect, etc.
@@ -399,12 +406,16 @@ namespace openvpn {
};
// Used to request an RSA signature.
// Data will be prefixed by an optional PKCS#1 digest prefix
// algorithm will determinate what signature is expected:
// RSA_PKCS1_PADDING means that
// data will be prefixed by an optional PKCS#1 digest prefix
// per RFC 3447.
// RSA_NO_PADDING mean so no padding should be done be the callee
struct ExternalPKISignRequest : public ExternalPKIRequestBase
{
std::string data; // data rendered as base64 (client reads)
std::string sig; // RSA signature, rendered as base64 (client writes)
std::string algorithm;
};
// used to override "remote" directives
@@ -597,7 +608,7 @@ namespace openvpn {
void on_disconnect();
// from ExternalPKIBase
virtual bool sign(const std::string& data, std::string& sig);
virtual bool sign(const std::string& data, std::string& sig, const std::string& algorithm);
// disable copy and assignment
OpenVPNClient(const OpenVPNClient&) = delete;

15
cmake/CMakeLists.txt.in Normal file
View File

@@ -0,0 +1,15 @@
cmake_minimum_required(VERSION 3.5)
project(googletest-download NONE)
include(ExternalProject)
ExternalProject_Add(googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG master
SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-src"
BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-build"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
TEST_COMMAND ""
)

9
cmake/FindLZ4.cmake Normal file
View File

@@ -0,0 +1,9 @@
find_path(LZ4_INCLUDE_DIR NAMES lz4.h)
find_library(LZ4_LIBRARY NAMES lz4)
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(
LZ4 DEFAULT_MSG
LZ4_LIBRARY LZ4_INCLUDE_DIR)
mark_as_advanced(LZ4_INCLUDE_DIR LZ4_LIBRARY)

63
cmake/FindmbedTLS.cmake Normal file
View File

@@ -0,0 +1,63 @@
# - Try to find mbedTLS
# Once done this will define
#
# Read-Only variables
# MBEDTLS_FOUND - system has mbedTLS
# MBEDTLS_INCLUDE_DIR - the mbedTLS include directory
# MBEDTLS_LIBRARY_DIR - the mbedTLS library directory
# MBEDTLS_LIBRARIES - Link these to use mbedTLS
# MBEDTLS_LIBRARY - path to mbedTLS library
# MBEDX509_LIBRARY - path to mbedTLS X.509 library
# MBEDCRYPTO_LIBRARY - path to mbedTLS Crypto library
FIND_PATH(MBEDTLS_INCLUDE_DIR mbedtls/version.h)
IF(MBEDTLS_INCLUDE_DIR AND MBEDTLS_LIBRARIES)
# Already in cache, be silent
SET(MBEDTLS_FIND_QUIETLY TRUE)
ENDIF()
FIND_LIBRARY(MBEDTLS_LIBRARY NAMES mbedtls libmbedtls libmbedx509)
FIND_LIBRARY(MBEDX509_LIBRARY NAMES mbedx509 libmbedx509)
FIND_LIBRARY(MBEDCRYPTO_LIBRARY NAMES mbedcrypto libmbedcrypto)
IF(MBEDTLS_INCLUDE_DIR AND MBEDTLS_LIBRARY AND MBEDX509_LIBRARY AND MBEDCRYPTO_LIBRARY)
SET(MBEDTLS_FOUND TRUE)
ELSEIF(MBEDTLS_INCLUDE_DIR AND MBEDTLS_LIBRARY AND NOT MBEDX509_LIBRARY AND NOT MBEDCRYPTO_LIBRARY)
SET(MBEDTLS_FOUND TRUE)
SET(HACKY_OVPN_MBEDTLS TRUE)
ENDIF()
IF(MBEDTLS_FOUND)
IF(HACKY_OVPN_MBEDTLS)
SET(MBEDTLS_LIBRARIES ${MBEDTLS_LIBRARY})
ELSE()
SET(MBEDTLS_LIBRARIES ${MBEDTLS_LIBRARY} ${MBEDX509_LIBRARY} ${MBEDCRYPTO_LIBRARY})
endif()
IF(NOT MBEDTLS_FIND_QUIETLY)
MESSAGE(STATUS "Found mbedTLS:")
FILE(READ ${MBEDTLS_INCLUDE_DIR}/mbedtls/version.h MBEDTLSCONTENT)
STRING(REGEX MATCH "MBEDTLS_VERSION_STRING +\"[0-9|.]+\"" MBEDTLSMATCH ${MBEDTLSCONTENT})
IF (MBEDTLSMATCH)
STRING(REGEX REPLACE "MBEDTLS_VERSION_STRING +\"([0-9|.]+)\"" "\\1" MBEDTLS_VERSION ${MBEDTLSMATCH})
MESSAGE(STATUS " version ${MBEDTLS_VERSION}")
ENDIF(MBEDTLSMATCH)
MESSAGE(STATUS " TLS: ${MBEDTLS_LIBRARY}")
MESSAGE(STATUS " X509: ${MBEDX509_LIBRARY}")
MESSAGE(STATUS " Crypto: ${MBEDCRYPTO_LIBRARY}")
ENDIF(NOT MBEDTLS_FIND_QUIETLY)
ELSE(MBEDTLS_FOUND)
IF(mbedTLS_FIND_REQUIRED)
MESSAGE(FATAL_ERROR "Could not find mbedTLS")
ENDIF(mbedTLS_FIND_REQUIRED)
ENDIF(MBEDTLS_FOUND)
MARK_AS_ADVANCED(
MBEDTLS_INCLUDE_DIR
MBEDTLS_LIBRARY_DIR
MBEDTLS_LIBRARIES
MBEDTLS_LIBRARY
MBEDX509_LIBRARY
MBEDCRYPTO_LIBRARY
)

35
cmake/dlgoogletest.cmake Normal file
View File

@@ -0,0 +1,35 @@
# Google Test Unit testing
# Download and unpack googletest at configure time
# Ensure that this only downloaded and added once
#include_guard(GLOBAL)
# Unfortunately include_guard requires cmake >= 3.10
include(mypragmaonce)
my_pragma_once()
configure_file(${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt.in googletest-download/CMakeLists.txt)
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
RESULT_VARIABLE result
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download )
if(result)
message(FATAL_ERROR "CMake step for googletest failed: ${result}")
endif()
execute_process(COMMAND ${CMAKE_COMMAND} --build .
RESULT_VARIABLE result
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download )
if(result)
message(FATAL_ERROR "Build step for googletest failed: ${result}")
endif()
# Prevent overriding the parent project's compiler/linker
# settings on Windows
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
# Add googletest directly to our build. This defines
# the gtest and gtest_main targets.
add_subdirectory(${CMAKE_CURRENT_BINARY_DIR}/googletest-src
${CMAKE_CURRENT_BINARY_DIR}/googletest-build
EXCLUDE_FROM_ALL)

107
cmake/findcoredeps.cmake Normal file
View File

@@ -0,0 +1,107 @@
cmake_minimum_required(VERSION 3.5)
set(CMAKE_CXX_STANDARD 14)
#cmake_policy(SET CMP0079 NEW)
set(CORE_DIR ${CMAKE_CURRENT_LIST_DIR}/..)
set(DEP_DIR ${CORE_DIR}/../deps CACHE PATH "Dependencies")
option(USE_MBEDTLS "Use mbed TLS instead of OpenSSL")
if (DEFINED ENV{DEP_DIR})
message(WARNING "Overriding DEP_DIR setting with environment variable")
set(DEP_DIR $ENV{DEP_DIR})
endif ()
# Include our DEP_DIR in path used to find libraries
function(add_core_dependencies target)
if (APPLE)
set(PLAT osx)
elseif (WIN32)
set(PLAT amd64)
else ()
set(PLAT linux)
endif ()
set(CORE_INCLUDES
${CORE_DIR}
${DEP_DIR}/asio/asio/include
)
set(CORE_DEFINES
-DASIO_STANDALONE
-DUSE_ASIO
-DHAVE_LZ4
-DLZ4_DISABLE_DEPRECATE_WARNINGS
-DMBEDTLS_DEPRECATED_REMOVED
)
if (WIN32)
list(APPEND CMAKE_PREFIX_PATH
${DEP_DIR}/${PLAT}/mbedtls
${DEP_DIR}/${PLAT}/lz4/lib
)
list(APPEND CMAKE_LIBRARY_PATH
${DEP_DIR}/${PLAT}/mbedtls/library
)
list(APPEND CORE_INCLUDES
${DEP_DIR}/${PLAT}/asio/asio/include
${DEP_DIR}/${PLAT}/lz4/lz4/include
${DEP_DIR}/${PLAT}/tap-windows/src
)
list(APPEND CORE_DEFINES
-D_WIN32_WINNT=0x0600
-DTAP_WIN_COMPONENT_ID=tap0901
-D_CRT_SECURE_NO_WARNINGS
)
set(EXTRA_LIBS fwpuclnt.lib Iphlpapi.lib)
target_compile_options(${target} PRIVATE "/bigobj")
else ()
list(APPEND CMAKE_PREFIX_PATH
${DEP_DIR}/mbedtls/mbedtls-${PLAT}
${DEP_DIR}/lz4/lz4-${PLAT}
)
list(APPEND CMAKE_LIBRARY_PATH
${DEP_DIR}/mbedtls/mbedtls-${PLAT}/library
)
endif ()
if (${USE_MBEDTLS})
find_package(mbedTLS REQUIRED)
set(SSL_LIBRARY ${MBEDTLS_LIBRARIES})
list(APPEND CORE_DEFINES -DUSE_MBEDTLS)
# The findmbedTLS does not set these automatically :(
list(APPEND CORE_INCLUDES ${MBEDTLS_INCLUDE_DIR})
else ()
find_package(OpenSSL REQUIRED)
SET(SSL_LIBRARY OpenSSL::SSL)
list(APPEND CORE_DEFINES -DUSE_OPENSSL)
endif ()
if (APPLE)
find_library(coreFoundation CoreFoundation)
find_library(iokit IOKit)
find_library(coreServices CoreServices)
find_library(systemConfiguration SystemConfiguration)
target_link_libraries(${target} ${coreFoundation} ${iokit} ${coreServices} ${systemConfiguration} ${lz4} ${SSL_LIBRARY})
endif()
if(UNIX)
target_link_libraries(${target} pthread)
endif()
find_package(LZ4 REQUIRED)
list(APPEND CORE_INCLUDES ${LZ4_INCLUDE_DIR})
target_include_directories(${target} PRIVATE ${CORE_INCLUDES})
target_compile_definitions(${target} PRIVATE ${CORE_DEFINES})
target_link_libraries(${target} ${SSL_LIBRARY} ${EXTRA_LIBS} ${LZ4_LIBRARY})
endfunction()

8
cmake/mypragmaonce.cmake Normal file
View File

@@ -0,0 +1,8 @@
macro(my_pragma_once)
set(__filename "${CMAKE_CURRENT_LIST_FILE}")
get_property(already_included GLOBAL PROPERTY "pr_${__filename}")
if(already_included)
return()
endif()
set_property(GLOBAL PROPERTY "pr_${__filename}" TRUE)
endmacro()

View File

@@ -1,4 +1,4 @@
From 28cdfe3f923affa87420a47f8ac71e791c77bcde Mon Sep 17 00:00:00 2001
From 00c0b1b7076ebc24735071f587f9501c1595a02b Mon Sep 17 00:00:00 2001
From: James Yonan <james@openvpn.net>
Date: Mon, 19 Mar 2018 11:24:10 +0800
Subject: [PATCH] Added Apple NAT64 support when both ASIO_HAS_GETADDRINFO and
@@ -16,10 +16,10 @@ Subject: [PATCH] Added Apple NAT64 support when both ASIO_HAS_GETADDRINFO and
1 file changed, 17 insertions(+)
diff --git a/asio/include/asio/detail/impl/socket_ops.ipp b/asio/include/asio/detail/impl/socket_ops.ipp
index b3b1a0cf..e1a07e06 100644
index ad203b74..b17a60ed 100644
--- a/asio/include/asio/detail/impl/socket_ops.ipp
+++ b/asio/include/asio/detail/impl/socket_ops.ipp
@@ -3338,6 +3338,23 @@ asio::error_code getaddrinfo(const char* host,
@@ -3339,6 +3339,23 @@ asio::error_code getaddrinfo(const char* host,
# endif
#elif !defined(ASIO_HAS_GETADDRINFO)
int error = getaddrinfo_emulation(host, service, &hints, result);
@@ -44,5 +44,5 @@ index b3b1a0cf..e1a07e06 100644
#else
int error = ::getaddrinfo(host, service, &hints, result);
--
2.16.2
2.21.0

View File

@@ -1,4 +1,4 @@
From c6cb856ac923472e56d8dd631585b4ca58e71c31 Mon Sep 17 00:00:00 2001
From fe57c9127cfc95b4673c6530f86edab5e6538425 Mon Sep 17 00:00:00 2001
From: James Yonan <james@openvpn.net>
Date: Wed, 2 Sep 2015 12:18:48 -0700
Subject: [PATCH] Added randomize() method to
@@ -9,7 +9,7 @@ Subject: [PATCH] Added randomize() method to
1 file changed, 7 insertions(+)
diff --git a/asio/include/asio/ip/basic_resolver_results.hpp b/asio/include/asio/ip/basic_resolver_results.hpp
index 4146a46b..f0ae258c 100644
index 3b3fad49..c070f7da 100644
--- a/asio/include/asio/ip/basic_resolver_results.hpp
+++ b/asio/include/asio/ip/basic_resolver_results.hpp
@@ -18,6 +18,7 @@
@@ -34,5 +34,5 @@ index 4146a46b..f0ae258c 100644
typedef std::vector<basic_resolver_entry<InternetProtocol> > values_type;
};
--
2.16.2
2.21.0

View File

@@ -1,4 +1,4 @@
From 69a6d6aec54b41f4ceac3ac2ba14465a36bf1984 Mon Sep 17 00:00:00 2001
From 80485636d5140c4b45679a4664eb7fe9e09f574f Mon Sep 17 00:00:00 2001
From: James Yonan <james@openvpn.net>
Date: Mon, 27 Feb 2017 13:01:26 -0700
Subject: [PATCH] Added user code hook async_connect_post_open() to be called
@@ -9,20 +9,20 @@ Subject: [PATCH] Added user code hook async_connect_post_open() to be called
1 file changed, 7 insertions(+)
diff --git a/asio/include/asio/basic_socket.hpp b/asio/include/asio/basic_socket.hpp
index 43430161..0d1b0d28 100644
index 42efbda4..4da85eb6 100644
--- a/asio/include/asio/basic_socket.hpp
+++ b/asio/include/asio/basic_socket.hpp
@@ -865,6 +865,8 @@ public:
asio::error_code ec;
@@ -950,6 +950,8 @@ public:
{
const protocol_type protocol = peer_endpoint.protocol();
this->get_service().open(this->get_implementation(), protocol, ec);
+ if (!ec)
+ async_connect_post_open(protocol, ec);
if (ec)
{
async_completion<ConnectHandler,
@@ -1741,6 +1743,11 @@ protected:
}
impl_.get_service().open(impl_.get_implementation(), protocol, open_ec);
+ if (!open_ec)
+ async_connect_post_open(protocol, open_ec);
}
return async_initiate<ConnectHandler, void (asio::error_code)>(
@@ -1800,6 +1802,11 @@ protected:
#endif
private:
+ // optional user code hook immediately after socket open in async_connect
@@ -34,5 +34,5 @@ index 43430161..0d1b0d28 100644
basic_socket(const basic_socket&) ASIO_DELETED;
basic_socket& operator=(const basic_socket&) ASIO_DELETED;
--
2.16.2
2.21.0

View File

@@ -0,0 +1,29 @@
From af733fe9ce8345e06947dcf8b395d4736b1cb98c Mon Sep 17 00:00:00 2001
From: Lev Stipakov <lev@openvpn.net>
Date: Mon, 29 Apr 2019 10:26:13 +0300
Subject: [PATCH] error_code.ipp: Use English for Windows error messages
FormatMessageA doesn't work well with non-ASCII chars
so make it return error message in English.
Signed-off-by: Lev Stipakov <lev@openvpn.net>
---
asio/include/asio/impl/error_code.ipp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/asio/include/asio/impl/error_code.ipp b/asio/include/asio/impl/error_code.ipp
index 55f5b361..3ef34fcd 100644
--- a/asio/include/asio/impl/error_code.ipp
+++ b/asio/include/asio/impl/error_code.ipp
@@ -80,7 +80,7 @@ public:
DWORD length = ::FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS, 0, value,
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char*)&msg, 0, 0);
+ MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), (char*)&msg, 0, 0);
detail::local_free_on_block_exit local_free_obj(msg);
if (length && msg[length - 1] == '\n')
msg[--length] = '\0';
--
2.16.2.windows.1

15
deps/lib-versions vendored
View File

@@ -1,5 +1,5 @@
export ASIO_VERSION=asio-1-12-0
export ASIO_CSUM=fa8c3a16dc2163f5b3451f2a14ce95277c971f46700497d4e94af6059c00dc06
export ASIO_VERSION=asio-1-13-0
export ASIO_CSUM=54a1208d20f2104dbd6b7a04a9262f5ab649f4b7a9faf7eac4c2294e9e104c06
export LZ4_VERSION=lz4-1.8.3
export LZ4_CSUM=33af5936ac06536805f9745e0b6d61da606a1f8b4cc5c04dd3cbaca3b9b4fc43
@@ -7,8 +7,6 @@ export LZ4_CSUM=33af5936ac06536805f9745e0b6d61da606a1f8b4cc5c04dd3cbaca3b9b4fc43
export MBEDTLS_VERSION=mbedtls-2.7.5
export MBEDTLS_CSUM=a1302ad9094aabb9880d2755927b466a6bac8e02b68e04dee77321f3859e9b40
export OPENSSL_VERSION=openssl-1.0.2h
export JSONCPP_VERSION=1.8.4
export JSONCPP_CSUM=c49deac9e0933bcb7044f08516861a2d560988540b23de2ac1ad443b219afdb6
@@ -16,4 +14,11 @@ export TAP_VERSION=0e30f5c13b3c7b0bdd60da915350f653e4c14d92
export TAP_CSUM=8ff65f9e741c5ecfe1af904eaa38713f05639ce9457ef92041fd8e6b2a170315
export CITYHASH_VERSION=8af9b8c2b889d80c22d6bc26ba0df1afb79a30db
export CITYHASH_CSUM=f70368facd15735dffc77fe2b27ab505bfdd05be5e9166d94149a8744c212f49
export CITYHASH_CSUM=f70368facd15735dffc77fe2b27ab505bfdd05be5e9166d94149a8744c212f49
export LZO_VERSION=lzo-2.10
export LZO_CSUM=c0f892943208266f9b6543b3ae308fab6284c5c90e627931446fb49b4221a072
export OPENSSL_VERSION=openssl-1.0.2s
export OPENSSL_CSUM=cabd5c9492825ce5bd23f3c3aeed6a97f8142f606d893df216411f07d1abab96

26
deps/lzo/build-lzo vendored
View File

@@ -5,6 +5,10 @@ if [ -z "$O3" ]; then
echo O3 var must point to ovpn3 tree
exit 1
fi
if [ -z "$DEP_DIR" ]; then
echo DEP_DIR var must point to dependency build folder
exit 1
fi
if [ -z "$TARGET" ]; then
echo TARGET var must be defined
exit 1
@@ -16,10 +20,24 @@ fi
. $O3/core/vars/vars-${TARGET}
. $O3/core/deps/lib-versions
[ "$GCC_CMD" ] && export CC=$GCC_CMD
[ "$LD_CMD" ] && export LD=$LD_CMD
[ "$AR_CMD" ] && export AR=$AR_CMD
[ "$RANLIB_CMD" ] && export RANLIB=$RANLIB_CMD
# source helper functions
. $O3/core/deps/functions.sh
FNAME=${LZO_VERSION}.tar.gz
PN=${LZO_VERSION#*-}
URL=http://www.oberhumer.com/opensource/lzo/download/${LZO_VERSION}.tar.gz
CSUM=${LZO_CSUM}
download
CC=cc
LD=ld
AR=ar
RANLIB=ranlib
[ "$GCC_CMD" ] && CC=$GCC_CMD
[ "$LD_CMD" ] && LD=$LD_CMD
[ "$AR_CMD" ] && AR=$AR_CMD
[ "$RANLIB_CMD" ] && RANLIB=$RANLIB_CMD
case $PLATFORM in
android*)

View File

@@ -44,6 +44,7 @@
namespace std {
%template(ClientAPI_ServerEntryVector) vector<openvpn::ClientAPI::ServerEntry>;
%template(ClientAPI_LLVector) vector<long long>;
%template(ClientAPI_StringVec) vector<string>;
};
// interface to be bridged between C++ and target language

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

60
openvpn/common/msfind.hpp Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

65
openvpn/common/strneq.hpp Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -59,6 +59,10 @@ namespace openvpn {
{
base64_uninit_static();
}
private:
// SSL library init happens when instantiated
crypto_init crypto_init_;
};
// process-wide singular instance

View File

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

72
openvpn/linux/cputime.hpp Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

336
openvpn/openssl/compat.hpp Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

242
openvpn/ssl/sess_ticket.hpp Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -36,6 +36,7 @@ if [ -z "$1" ]; then
echo " MTLS_PATH=path -- use user specified mbedTLS source folder"
echo " MTLS_LIBS=ldflags -- user specific mbedTLS LDFLAGS"
echo " MA_HYBRID=1 -- use mbedTLS/AppleCrypto hybrid"
echo " OPENSSL_DIST=<dir> -- specify custom OpenSSL build"
echo " NOSSL=1 -- don't include OpenSSL"
echo " OPENSSL_SYS=1 -- include system OpenSSL"
echo " MINI=1 -- link with OpenSSL mini crypto lib"
@@ -148,7 +149,11 @@ elif [ "$MTLS" = "1" ]; then
fi
# OpenSSL
if [ "$APPLE_FAMILY" = "1" ]; then
if [ "$OPENSSL_DIST" ]; then
CPPFLAGS="$CPPFLAGS -DUSE_OPENSSL -I$OPENSSL_DIST/include"
LIBDIRS="$LIBDIRS -L$OPENSSL_DIST/lib"
LIBS="$LIBS -lssl -lcrypto"
elif [ "$APPLE_FAMILY" = "1" ]; then
# On Mac, only link with OpenSSL if OSSL is defined.
# On other platforms, usually link with OpenSSL.
if [ "$OPENSSL_SYS" == "1" ]; then
@@ -181,7 +186,7 @@ else
LIBS="$LIBS -lssl -lcrypto -ldl"
fi
fi
if [ "$OPENSSL_SYS" != "1" ] && [ "$OPENSSL_LINK" != "1" ] && [ "$NOSSL" != "1" ]; then
if [ "$OPENSSL_SYS" != "1" ] && [ "$OPENSSL_LINK" != "1" ] && [ "$NOSSL" != "1" ] && [ -z "$OPENSSL_DIST" ]; then
CPPFLAGS="$CPPFLAGS -I$DEP_DIR/openssl/openssl-$PLATFORM/include"
LIBDIRS="$LIBDIRS -L$DEP_DIR/openssl/openssl-$PLATFORM/lib"
fi

View File

@@ -21,7 +21,7 @@ if [ "$OSX_ONLY" != "1" ]; then
done
fi
for target in osx osx-dbg ; do
for target in osx64 osx64-dbg ; do
echo '***************' TARGET $target
TARGET=$target $O3/core/deps/lzo/build-lzo
done

View File

@@ -0,0 +1,13 @@
cmake_minimum_required(VERSION 3.5)
project(ovpncli)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake;${CMAKE_MODULE_PATH}")
include(findcoredeps)
add_executable(ovpncli
cli.cpp)
add_core_dependencies(ovpncli)

View File

@@ -84,13 +84,126 @@
#include "client/core-client-netcfg.hpp"
#endif
#if defined(OPENVPN_PLATFORM_LINUX)
// use SITNL by default
#ifndef OPENVPN_USE_IPROUTE2
#define OPENVPN_USE_SITNL
#endif
#include <openvpn/tun/linux/client/tuncli.hpp>
// we use a static polymorphism and define a
// platform-specific TunSetup class, responsible
// for setting up tun device
#define TUN_CLASS_SETUP TunLinuxSetup::Setup<TUN_LINUX>
#elif defined(OPENVPN_PLATFORM_MAC)
#include <openvpn/tun/mac/client/tuncli.hpp>
#define TUN_CLASS_SETUP TunMac::Setup
#endif
using namespace openvpn;
namespace {
OPENVPN_SIMPLE_EXCEPTION(usage);
}
class Client : public ClientAPI::OpenVPNClient
#ifdef USE_TUN_BUILDER
class ClientBase : public ClientAPI::OpenVPNClient
{
public:
bool tun_builder_new() override
{
tbc.tun_builder_set_mtu(1500);
return true;
}
int tun_builder_establish() override
{
if (!tun)
{
tun.reset(new TUN_CLASS_SETUP());
}
TUN_CLASS_SETUP::Config config;
config.layer = Layer(Layer::Type::OSI_LAYER_3);
// no need to add bypass routes on establish since we do it on socket_protect
config.add_bypass_routes_on_establish = false;
return tun->establish(tbc, &config, nullptr, std::cout);
}
bool tun_builder_add_address(const std::string& address,
int prefix_length,
const std::string& gateway, // optional
bool ipv6,
bool net30) override
{
return tbc.tun_builder_add_address(address, prefix_length, gateway, ipv6, net30);
}
bool tun_builder_add_route(const std::string& address,
int prefix_length,
int metric,
bool ipv6) override
{
return tbc.tun_builder_add_route(address, prefix_length, metric, ipv6);
}
bool tun_builder_reroute_gw(bool ipv4,
bool ipv6,
unsigned int flags) override
{
return tbc.tun_builder_reroute_gw(ipv4, ipv6, flags);
}
bool tun_builder_set_remote_address(const std::string& address,
bool ipv6) override
{
return tbc.tun_builder_set_remote_address(address, ipv6);
}
bool tun_builder_set_session_name(const std::string& name) override
{
return tbc.tun_builder_set_session_name(name);
}
bool tun_builder_add_dns_server(const std::string& address, bool ipv6) override
{
return tbc.tun_builder_add_dns_server(address, ipv6);
}
void tun_builder_teardown(bool disconnect) override
{
std::ostringstream os;
auto os_print = Cleanup([&os](){ OPENVPN_LOG_STRING(os.str()); });
tun->destroy(os);
}
bool socket_protect(int socket, std::string remote, bool ipv6) override
{
(void)socket;
std::ostringstream os;
auto os_print = Cleanup([&os](){ OPENVPN_LOG_STRING(os.str()); });
return tun->add_bypass_route(remote, ipv6, os);
}
private:
TUN_CLASS_SETUP::Ptr tun = new TUN_CLASS_SETUP();
TunBuilderCapture tbc;
};
#else // USE_TUN_BUILDER
class ClientBase : public ClientAPI::OpenVPNClient
{
public:
bool socket_protect(int socket, std::string remote, bool ipv6) override
{
std::cout << "NOT IMPLEMENTED: *** socket_protect " << socket << " " << remote << std::endl;
return true;
}
};
#endif
class Client : public ClientBase
{
public:
enum ClockTickAction {
@@ -145,13 +258,6 @@ public:
#endif
private:
bool socket_protect(int socket, std::string remote, bool ipv6) override
{
std::cout << "*** socket_protect " << socket << " "
<< remote << std::endl;
return true;
}
virtual void event(const ClientAPI::Event& ev) override
{
std::cout << date_time() << " EVENT: " << ev.name;
@@ -596,6 +702,7 @@ int openvpn_client(int argc, char *argv[], const std::string* profile_content)
{ "force-aes-cbc", no_argument, nullptr, 'f' },
{ "google-dns", no_argument, nullptr, 'g' },
{ "persist-tun", no_argument, nullptr, 'j' },
{ "wintun", no_argument, nullptr, 'w' },
{ "def-keydir", required_argument, nullptr, 'k' },
{ "merge", no_argument, nullptr, 'm' },
{ "version", no_argument, nullptr, 'v' },
@@ -651,6 +758,7 @@ int openvpn_client(int argc, char *argv[], const std::string* profile_content)
bool autologinSessions = false;
bool retryOnAuthFailed = false;
bool tunPersist = false;
bool wintun = false;
bool merge = false;
bool version = false;
bool altProxy = false;
@@ -664,7 +772,7 @@ int openvpn_client(int argc, char *argv[], const std::string* profile_content)
int ch;
optind = 1;
while ((ch = getopt_long(argc, argv, "BAdeTCxfgjmvaYu:p:r:D:P:6:s:t:c:z:M:h:q:U:W:I:G:k:X:R:", longopts, nullptr)) != -1)
while ((ch = getopt_long(argc, argv, "BAdeTCxfgjwmvaYu:p:r:D:P:6:s:t:c:z:M:h:q:U:W:I:G:k:X:R:", longopts, nullptr)) != -1)
{
switch (ch)
{
@@ -769,6 +877,9 @@ int openvpn_client(int argc, char *argv[], const std::string* profile_content)
case 'j':
tunPersist = true;
break;
case 'w':
wintun = true;
break;
case 'm':
merge = true;
break;
@@ -863,6 +974,7 @@ int openvpn_client(int argc, char *argv[], const std::string* profile_content)
config.tunPersist = tunPersist;
config.gremlinConfig = gremlin;
config.info = true;
config.wintun = wintun;
#if defined(OPENVPN_OVPNCLI_SINGLE_THREAD)
config.clockTickMS = 250;
#endif
@@ -1040,6 +1152,7 @@ int openvpn_client(int argc, char *argv[], const std::string* profile_content)
std::cout << "--auto-sess, -a : request autologin session" << std::endl;
std::cout << "--auth-retry, -Y : retry connection on auth failure" << std::endl;
std::cout << "--persist-tun, -j : keep TUN interface open across reconnects" << std::endl;
std::cout << "--wintun, -w : use WinTun instead of TAP-Windows6 on Windows" << std::endl;
std::cout << "--peer-info, -I : peer info key/value list in the form K1=V1,K2=V2,..." << std::endl;
std::cout << "--gremlin, -G : gremlin info (send_delay_ms, recv_delay_ms, send_drop_prob, recv_drop_prob)" << std::endl;
std::cout << "--epki-ca : simulate external PKI cert supporting intermediate/root certs" << std::endl;

View File

@@ -0,0 +1,51 @@
cmake_minimum_required(VERSION 3.5)
project(tests)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake;${CMAKE_MODULE_PATH}")
include(findcoredeps)
include(dlgoogletest)
# Extra includes/libraries that are currently only use by the core unit test
FIND_PATH(LZO_INCLUDE_DIR NAMES lzo/lzo1x.h)
FIND_LIBRARY(LZO_LIBRARIES NAMES lzo2)
if (LZO_INCLUDE_DIR AND LZO_LIBRARIES)
list(APPEND CORE_TEST_DEFINES -DHAVE_LZO)
list(APPEND EXTRA_LIBS ${LZO_LIBRARIES})
list(APPEND EXTRA_INCLUDES ${LZO_INCLUDE_DIR})
message("lzo found, running lzo compression tests")
else ()
message("lzo not found, skipping lzo compression tests")
endif ()
set(SOURCES
core_tests.cpp
test_route_emulation.cpp
test_log.cpp
test_comp.cpp
test_b64.cpp
)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
list(APPEND SOURCES test_sitnl.cpp test_cpu_time.cpp)
endif ()
set(CORE_TEST_DEFINES
-DOPENVPN_FORCE_TUN_NULL
-DUNIT_TEST
-DUNITTEST_SOURCE_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}/\"
)
add_executable(coreUnitTests ${SOURCES})
add_core_dependencies(coreUnitTests)
target_link_libraries(coreUnitTests gtest_main ${EXTRA_LIBS})
target_compile_definitions(coreUnitTests PRIVATE ${CORE_TEST_DEFINES})
target_include_directories(coreUnitTests PRIVATE ${EXTRA_INCLUDES})
add_test(NAME CoreTests COMMAND coreUnitTests)

View File

@@ -0,0 +1,3 @@
To test and compare speed of the decomrpession algorithms define N_EXPAND to be more than 1 by either edition
the test-comp.c or by add a -DN_EXPAND=1000 or -DN_COMPRESS=1000 for the compression speed in the CmakeLists
and looks at the time the unit test take for the different combinations.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,645 @@
<head>
<title>Compression Pointers</title>
<META HTTP-EQUIV="Keywords" CONTENT="compression, compression, compression">
</head>
<body>
<BODY BGCOLOR=#FFFFFF>
<center>
<H1> Compression Pointers </h1>
</center>
<p>
<a href="#Resources">Compression resources</a>, <a href="#Conferences">conferences</a>, and some <a href="#Research">research
groups and companies</a>, are listed towards the end of this page. <p>
Use this <a href="form.html">handy form</a> to add something to this page, or to simply say you liked this page<i> <img src="new.gif">
<p>
</i>
<p>
<h2>What's New?</h2><p>
<a href="http://www.teaser.fr/~jlgailly/">Jean-loup Gailly</a> -- <i>Mr. gzip,
PNG, CCR (1996-06-10)</i>
<img src="new.gif">
<br>
<a href="http://www.creative.net/~tristan/MPEG">MPEG Pointers and Resources</a>
<img src="new.gif">
<br>
<a href="http://www-isl.stanford.edu/~gray/">Robert M. Gray</a> -- <i>Signal
compression, VQ, image quality evaluation (1996-04-22)</i><img src="new.gif">
<br>
<a href="http://www-isl.stanford.edu/~gray/compression.html">Compression and
Classification Group</a> -- <i>ISL,EE,Stanford (1996-04-22)</i><img src="new.gif">
<br>
<a href="http://www-isl.stanford.edu/~gray/iii.html">Signal Processing
and the International Information Infrastructure</a> -- <i>Web sites (1996-04-22)</i> <img src="new.gif">
<br>
<a href="http://www.cs.sc.edu:80/~valenta/">Valenta, Vladimir</a> -- <i>(1996-04-22)</i>
<br>
<a href="http://patpwww.epfl.ch:80/~jordan/Subjects/Parallel/parallel.html">Jordan, Frederic</a> -- <i>Parallel Image Compression (1996-04-12)</i> <img src="new.gif">
<br>
<a href="http://www.garlic.com/biz/eotek">Electro-Optical Technologies, Inc.</a> -- <i>Consultant (1996-04-12)</i> <img src="new.gif">
<br>
<a href="http://quicktime.apple.com">quicktime.apple.com</a> -- <i>Quicktime site (1996-03-11)</i> <img src="new.gif">
<br>
<a href="http://www.chips.ibm.com/products/aldc/index.html">IBM Hardware</a> -- <i>Compression chips (1996-03-11)</i> <img src="new.gif">
<br>
<!---
<img src="new.gif"><i>Do you have, or know of, a research position where an image compression guru with a Ph.D. would feel at
home? Let me know if you do...<a
href="mailto:singlis@cs.waikato.ac.nz"><b>send me some email!</b></a>, or <a
href="http://www.cs.waikato.ac.nz/~singlis/cv.html"><b>check out my
c.v.</b>
</a>
</i>
-->
<p>
<p>
<h2><i>People</i></h2>
<h2>A</h2>
<a href="http://quest.jpl.nasa.gov/Mark.Adler/">Adler, Mark</a> -- <i>Info-ZIP; Zip, UnZip, gzip and zlib co-author; PNG group</i>
<p>
<h2>B</h2>
<a href="http://www.eese.qut.edu.au/~mbaker/">Baker, Matthew</a> -- <i>Region based video compression</i>
<p>
<a href="http://www.eecs.wsu.edu/~bamberg/">Bamberger, Roberto H.</a>
<p>
<a href="http://sutherland.eese.qut.edu.au/~dbell">Bell, Daniel</a> -- <i>Region based image compression</i>
<p>
<a href="http://www.cosc.canterbury.ac.nz/~tim">Bell, Tim</a> -- <i>compression, computer science for children, and computers and music.</i>
<p>
<a href="http://www.polytechnique.fr/poly/~bellard/">Bellard, Fabrice</a> -- <i>Author of LZEXE</I>
<p>
<a href="http://www.cevis.uni-bremen.de/~willy/">Berghorn, Willy </a>
<p>
<a href="http://www.best.com/~bhaskara">Bhaskaran, Vasudev</a> -- <i>Image and Video compression</i>
<p>
<a href="http://wwwvms.utexas.edu/~cbloom/index.html">Bloom, Charles</a> -- <i>text compression, LZ methods, PPM methods...</i>
<p>
<a href="http://www.ime.usp.br/~rbrito">Brito, Roger</a> -- <i>Arithmetic Coding, LZW, Text Compression</i>
<p>
<a href="http://www.engineering.usu.edu/ece/faculty/scottb.html">Budge, Scott E.</a> -- <i>medical/lossy image compression</i>
<p>
<a href="http://www.elec.uow.edu.au/people/staff/burnett/biography.html">Burnett, Ian</a> -- <i>Speech coding, prototype waveform techniques</I> <img src="new.gif">
<p>
<h2>C</h2>
<a href="http://gabor.eecs.berkeley.edu/~wychan/">Chan, Christopher</a> -- <i>universal lossy source coding, adaptive VQ</i>
<p>
<a href="http://www.compsci.com/~chao">Chao, Hong-yang</a> -- <I>Lightning strike image compressor </i>
<p>
<a href="http://monet.uwaterloo.ca:80/schao/">Chao, Stewart</a>
<p>
<a href="http://diana.ecs.soton.ac.uk/~pjc94r/">Cherriman, Peter</a>
<p>
<a href="http://www-video.eecs.berkeley.edu/erl/sccheung.html">Cheung, S.C.</a> -- <i>Scalable video compression algorithms (18th Feb 1996)</i> <img src="new.gif">
<p>
<a href="http://etro.vub.ac.be/chchrist.html">Christopoulos, Charilaos</a> -- <i> Image and Video compression </i>
<p>
<a href="http://sipi.usc.edu/~chrysafi/">Chrysafis, Christos</a>
<p>
<a href="http://www.ee.gatech.edu/research/DSP/students/wchung/index.html">Chung, Wilson C.</a> -- <i>R-D image and video coding, subband/wavelet, filter banks</i>
<p>
<a href="mailto:aclark@hayes.com">Clark, Alan</a> -- <i>primary contact for V.42bis. Developed BTLZ variant of LZW</i>
<p>
<a href="http://www.cs.waikato.ac.nz/cs/Staff/jgc.html">Cleary, John</a> -- <i>PPM, PPM*, K*</i>
<p>
<a href="http://www.cs.brandeis.edu/dept/faculty/cohn/">Cohn, Martin</a>
<p>
<a href="http://plg.uwaterloo.ca/~gvcormac">Cormack, Gordon V.</a> -- <I>DMC</i>
<p>
<a href="http://www.ee.duke.edu/~cec/index.html">Cramer, Chris</a> -- <i>neural network image compression</i>
<p>
<h2>D</h2>
<a href="http://www.cs.dartmouth.edu/~jmd/">Danskin, John</a> -- <i>Protocol compression, document compression (9th Feb 1996)</i>
<p>
<a href="http://www.cs.dartmouth.edu/~gdavis">Davis, Geoff</a> -- <i>wavelets, image compression, medical</i>
<p>
<a href="http://www-mddsp.enel.ucalgary.ca/People/adilger/">Dilger, Andreas</a> -- <i>fractal block coding of video sequences</i>
<p>
<h2>E</h2>
<a href="http://info.cipic.ucdavis.edu/~estes/index.html">Estes, Robert</a>
<p>
<h2>F</h2>
<a href="http://inls.ucsd.edu/y/Fractals/">Fisher, Yuval</a> -- <i>Fractal Image Compression</i>
<p>
<a href="http://www.wmin.ac.uk/~ajoec1/homepage.html">Ford, Adrian</a> -- <i>Subjective and Objective Compression quality (5th Mar 1996)</i> <img src="new.gif">
<p>
<h2>G</h2>
<a href="http://www.teaser.fr/~jlgailly/">Jean-loup Gailly</a> --
<i>Mr. gzip, PNG, CCR (1996-06-10)</i>
<p>
<a href="http://www.ece.ucsb.edu/faculty/gersho/default.html">Gersho, Allen</a> -- <i>Vector Quantisation, Image Compression</i>
<p>
<a href="http://life.anu.edu.au/ci/vol1/goertzel.html">Goertzel, Ben</a>
<p>
<a href="http://info.lut.ac.uk/departments/el/research/sys/compression.html">Gooch, Mark</a> -- <i>High Performance Hardware Compression (7th Feb 1996)</i>
<p>
<a href="http://robotics.eecs.berkeley.edu/~vkgoyal/">Goyal, Vivek</a> -- <i>overcomplete representations, adaptive transform coding, VQ</i>
<p>
<a href="http://www-isl.stanford.edu/~gray/">Robert M. Gray</a> -- <i>Signal
compression, VQ, image quality evaluation</i><img src="new.gif">
<p>
<h2>H</h2>
<a href="http://cork.informatik.uni-wuerzburg.de/mitarbeiter/ulli">Hafner, Ullrich</a> -- <i>WFA image compression</i>
<p>
<a href="http://www.ics.uci.edu/~dan/">Hirschberg, Dan</a> -- <i>algorithm theory, compression</i>
<p>
<a href="http://wolfpack.larc.nasa.gov/holland.html">Holland, Scott</a>
<p>
<a href="http://www.csc.uvic.ca/home/nigelh/nigelh.html">Horspool, R. Nigel</a> -- <i>text compression, ECG compression</i>
<h2>I</h2>
<a href="http://www.cs.waikato.ac.nz/~singlis">Inglis, Stuart</a> -- <i>image compression, OCR, lossy/lossless document compression</I>
<p>
<h2>J</h2>
<a href="http://www.cs.uiowa.edu/~jones/compress/index.html">Jones, Douglas</a> -- <i>splay-tree based compression and encryption</i>
<p>
<a href="http://info.lut.ac.uk/departments/el/research/sys/compression.html">Jones, Simon</a> -- <i>Lossless compression, High Performance Hardware (7th Feb 1996) </i>
<p>
<a href="http://patpwww.epfl.ch:80/~jordan/Subjects/Parallel/parallel.html">Jordan, Frederic</a> -- <i>Parallel Image Compression (12th April 1996)</i> <img src="new.gif">
<p>
<a href="mailto:robjung@world.std.com">Jung, Robert K.</a> -- <i><a href="http://www.info.fundp.ac.be/~fta/arj.html">ARJ</a> </i>
<p>
<a href="http://www.cs.tu-berlin.de/~jutta/toast.html">jutta</a>
<p>
<h2>K</h2>
<a href="http://links.uwaterloo.ca">Kominek, John</a> -- <I>Fractal and spline based compression</i>
<p>
<a href="http://info.lut.ac.uk/departments/el/research/sys/elmk3.html">Kjelso, Morten</a> -- <i>Main memory compression, High performance hardware</i>
<p>
<a href="http://wwwcip.informatik.uni-erlangen.de/user/mskuhn">Kuhn, Markus</a> -- <i>JBIG implementation</i> <img src="new.gif">
<p>
<a href="http://sipi.usc.edu/faculty/Kuo.html">Kuo, C.-C. Jay</a>
<p>
<a href="http://kaarna.cc.jyu.fi:80/~kuru/">Kuru, Esa</a>
<p>
<a href="http://www.prz.tu-berlin.de:80/~teo/">Kyfonidis, Theodoros</a>
<p>
<h2>L</h2>
<a href="http://www.icsi.berkeley.edu/~lampart/">Lamparter, Bernd</a>
<p>
<a href="http://www.cse.ucsc.edu/cgi-bin/faculty?langdon">Langdon, Glen</a>
<p>
<a href="http://www.dna.lth.se/~jesper/">Larsson, Jesper</a> -- <i>algorithms and data structures, text compression</i> <img src="new.gif">
<p>
<h2>M</h2>
<a href="http://glimpse.cs.arizona.edu:1994/udi.html">Manber, Udi</a>
<p>
<a href="http://www.elec.rma.ac.be/~jma/compression.html">Mangen, Jean-Michel</a> -- <i>image compression, wavelets, satellite</I>
<p>
<a href="http://www.cs.mu.oz.au/~alistair">Moffat, Alistair</a> -- <i>text and index compression, coding methods, information retrieval, document databases</i>
<p>
<a href="http://deskfish.cs.titech.ac.jp/squish/squish_index.html">Montgomery, Christopher</a> -- <i>OggSquish, audio compression</i>
<p>
<a href="mailto:umueller@amiga.physik.unizh.ch">Mueller, Urban Dominik</a> -- <i>XPK system</I>
<p>
<h2>N</h2>
<a href="ftp://ai.toronto.edu/pub/radford/www/index.html">Neal, Radford</a>
<p>
<a href="http://www.cs.waikato.ac.nz/~cgn">Nevill-Manning, Craig</a>
<p>
<a href="http://phoenix.bath.ac.uk/jez/jez.html">Nicholls, Jeremy</a>
<p>
<a href="http://www.acti.com/matt/">Noah, Matt</a> -- <i>speech, ATC, ACELP and IMBE</i>
<p>
<a href="http://www.eecs.wsu.edu/~vnuri/">Nuri, Veyis</a> -- <i>wavelets, data compression, DSP (speech & image)</I>
<h2>O</h2>
<a href="ftp://replicant.csci.unt.edu/pub/oleg/README.html">Oleg</a> -- <i>Lots of code</i>
<p>
<a href="http://sipi.usc.edu/faculty/Ortega/Ortega.html">Ortega, Antonio</a> -- <i>Video compression, Packet video, Adaptive quantization</i>
<p>
<a href="http://www.cs.dartmouth.edu:80/~cowen/">Owen, Charles</a>
<p>
<h2>P</h2>
<a
href="http://www-mddsp.enel.ucalgary.ca/People/provine/compression.html">Provine,
Joseph</a> -- <i>Model based coding</i>
<p>
<h2>R</h2>
<a href="http://monet.uwaterloo.ca/~john/btpc.html">Robinson, John</a> -- <i>Binary Tree Predictive Coding</i>
<p>
<a href="http://quest.jpl.nasa.gov/Info-ZIP/people/greg/">Roelofs, Greg</a> -- <i>Info-ZIP; primary UnZip author; PNG group</i>
<p>
<a href="http://www.ece.ucsb.edu/faculty/rose/default.html">Rose, Kenneth</a>
<p>
<a href="http://www.nettuno.it/fiera/telvox/telvox.htm">Russo, Roberto Maria</a> -- <I>Multiplatform Data Compression</i>
<p>
<h2>S</h2>
<a href="http://harc.edu:80/~schmidt/">Schmidt, Bill</a>
<p>
<a href="http://koa.ifa.hawaii.edu/~shaw/shaw.html">Shaw, Sandy C.</a>
<p>
<a href="http://www.comm.toronto.edu/~kamran/kamran.html">Sharifi, Kamran</a> -- <i>Video over ATM networks</i>
<p>
<a href="http://calypso.unl.edu/~sunil/">Shende, Sunil M.</a>
<p>
<a href="http://viewprint2.berkeley.edu/KleinLab/Amnon/Amnon.html">Silverstein, D. Amnon</a>
<p>
<a href="http://www-plateau.cs.berkeley.edu/people/smoot">Smoot, Steve</a> -- <i>MPEG</i>
<p>
<a href="http://www.cs.brandeis.edu/dept/faculty/storer/">Storer, James A.</a>
<p>
<a href="http://rice.ecs.soton.ac.uk/index.html">Streit, Jurgen</a> -- <i>Low bitrate coding</i>
<p>
<a href="http://www_nt.e-technik.uni-rostock.de/~ts/index.html">Strutz, Tilo</a> -- <i>Wavelet image compressor, demonstration execs</i> <img src="new.gif">
<p>
<h2>T</h2>
<a href="http://zaphod.csci.unt.edu:80/~srt/">Tate, Steve</a>
<p>
<a href="http://www.cs.waikato.ac.nz/~wjt">Teahan, Bill</a> -- <i>PPM, PPM*, master of the Calgary Corpus</i>
<p>
<a href="http://lightning.eee.strath.ac.uk/~duncan/">Thomson, Duncan</a> -- <i>ECG compression with ANNs</i>
<p>
<a href="http://outside.gsfc.nasa.gov/GRSS/">Tilton, James C.</a>
<p>
<a href="http://www.uni-karlsruhe.de/~un55/">Tomczyk, Marek</a>
<p>
<a href="http://munkora.cs.mu.oz.au/~aht">Turpin, Andrew</a> -- <i>Prefix Codes</i>
<p>
<h2>V</h2>
<a href="http://gabor.eecs.berkeley.edu/~martin/">Vetterli, Martin</a> -- <i>wavelets, subband coding, video compression, computational complexity</i>
<p>
<a href="http://www.cs.duke.edu/~jsv/HomePage.html">Vitter, Jeff</a>
<p>
<a href="http://www.eee.strath.ac.uk/~stathis/home.html">Voukelatos, Stathis</a>
<p>
<a href="http://www.xs4all.nl/~aipnl">de Vries, Nico</a> -- <i>AIP-NL, UltraCompressor II development</i> <img src="new.gif">
<p>
<h2>W</h2>
<a href="http://ipcl.ee.queensu.ca/wareham/wareham.html">Wareham, Paul</a> -- <i>Region-oriented video coding</I>
<p>
<a href="mailto://Wegs59@aol.com">Wegener, Al</a> -- <i>DSP, lossless audio compression and AC-2</i>
<p>
<a href="http://www-dsp.rice.edu:80/~weid/">Wei, Dong</a> -- <i>Wavelet compression</i>
<p>
<a href="http://www.rocksoft.com/~ross">Williams, Ross</a>
<p>
<a href="http://www.cs.waikato.ac.nz/~ihw">Witten, Ian</a> -- <i>PPM, mg, arithmetic coding</i>
<p>
<a href="http://www.sees.bangor.ac.uk/~gerry/sp_summary.html">Wolff, Gerry</a>
<p>
<a href="http://sipi.usc.edu/~wwoo/">Woo, Woon-Tack</a> -- <i>stereo image and video compression (12th Feb 1996)</i>
<p>
<a href="http://www.csd.uwo.ca/faculty/wu/">Wu, Xiaolin</a> -- <i>CALIC</i>
<P>
<h2>Z</h2>
<a href="http://www.cs.rmit.edu.au/~jz">Zobel, Justin</a> -- <i>index compression, database compression</i>
<p>
<br>
<br>
<a name="Conferences">
<h2><i>Conferences</i></h2>
<a href="http://www.cs.brandeis.edu:80/~dcc/index.html"><b>Data Compression Conference (DCC), Snowbird, Utah, USA.</b></a><br>
<p>
<a name="Resources">
<h2><i>Resources</i></h2>
<dt>
<i><b>Where to get answers...</b></i>
<dd>
<dd><a href="http://info.itu.ch/">International Telecommunication Union (ITU)</a> -- <i>standards documents (CCITT)</i>
<br>
<dd><a href="http://www.cis.ohio-state.edu/hypertext/faq/usenet/compression-faq/top.html">comp.compression -- Frequently Asked Questions</a> -- <i>Come here 1st!</i>
<dd>
<a href="http://www.cis.ohio-state.edu/hypertext/faq/usenet/jpeg-faq/faq.html">JPEG - Frequently Asked Questions</a>
<dd>
<a href="http://www.crs4.it/HTML/LUIGI/MPEG/mpegfaq.html">MPEG - Frequently Asked Questions</a>
<dd>
<a href="ftp://rtfm.mit.edu:/pub/usenet/news.answers/standards-faq">Standards - Frequenty Asked Questions</a>
<dd>
<a href="news:comp.compression.research">usenet newsgroup: comp.compression.research</a>
<br>
<dd>
<a href="http://www.mi.net/act/act.html">Archive Compression Test</a> -- <i>Summary of all archivers</i>
<br>
<dd>
<a href="http://dip1.ee.uct.ac.za/fractal.bib.html">Fractal compression bibliography</a> -- <i>bibtex file</i>
<br>
<dd>
<a href="http://goethe.ira.uka.de/edu/proseminar.html">LZ/Complexity Seminars</a> -- <i>(in German)</i>
<br>
<dd>
<a href="http://www.cs.waikato.ac.nz/~singlis/ratios.html">Comparitive Compression Ratios</a> -- <i>Sofar...B&W, Gray Scale, Text Compression</i>
<br>
<dd>
<a href="http://www.cs.ucl.ac.uk/mice/mpeggloss.html">VCN (Video, Compression, Networking)
Glossary</a>
<br>
<dd>
<a href="http://www.cs.waikato.ac.nz/~nzdl/">Digital Library -- search for compression</a>
<br>
</dt>
<br>
<dt>
<i><b>Reports/Ph.D. Theses</b></i>
<dd>
<a href="http://www.cs.brown.edu/publications/techreports/reports/CS-93-28.html">Paul Howards Ph.D. thesis</a><br>
<dd><i>The Design and Analysis of Efficient Lossless Data Compression Systems</i>
<br>
</dt>
<br>
<dt>
<i><b>Source code</b></i>
<dd>
<a href="ftp://munnari.oz.au/pub/arith_coder">Arithmetic coding routines</a>
<dd><i>&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp(from Moffat, Neal and Witten, Proc. DCC, April 1995)</i>
<br>
<dd>
<a href="ftp://ftp.cpsc.ucalgary.ca/pub/projects/ar.cod">CACM Arithmetic coding package</a>
<dd><i>&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp(Witten, Neal and Cleary, CACM 30:520-541, June 1987)</i>
<br>
<dd>
<a href="ftp://nic.funet.fi/pub/graphics/misc/test-images/jbig.tar.gz">JBIG Source code</a> <i>Includes a Q-coder</i>
<br>
<dd><a href="http://wwwcip.informatik.uni-erlangen.de/user/mskuhn">Markus Kuhn's JBIG implementation</a>
<br>
<dd>
<a href="ftp://media-lab.media.mit.edu/pub/k-arith-code/">k-arithmetic coder</a>
<br>
<dd>
<a href="ftp://garbo.uwasa.fi/pc/programming/lds_11.zip">Lossless Data Compression toolkit 1.1</a>
<br>
<dd>
<a href="http://www.cs.uiowa.edu/~jones/compress/index.html">Splay Trees Code</a> -- <i>by Douglas W. Jones</i>
<br>
<dd><a href="ftp://ftp.cl.cam.ac.uk/users/djw3">Block compression code</a> -- <i>Excellent text compressor</i>
<br>
</dt>
<br>
<dt>
<i><b>Test Files</b></i>
<dd>
<a href="ftp://nic.funet.fi/pub/graphics/misc/test-images/">Test Images</a> -- <i>CCITT images (pbm), Lena etc. (Sun raster)</i>
<br>
<dd>
Stockholm test images -- <i>You have to buy a CDROM, thats all I know! :-(</i>
<dd>&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp mail me at <a href="mailto:singlis@cs.waikato.ac.nz">singlis@cs.waikato.ac.nz</a> if you know anything about them.
<br>
<dd>
<a href="ftp://ftp.cpsc.ucalgary.ca/pub/projects/text.compression.corpus/">Calgary Text Compression Corpus</a><i> (Text Compression, Bell, Cleary and Witten, 1990)</i>
<br>
</dt>
<br>
<a name="Research">
<h2><i>Research Projects, Standards & Companies</i></h2>
<p>
<dt>
<i><b>Research/Free software Groups</b></i>
<dd><a href="http://www-isl.stanford.edu/~gray/compression.html">Compression and
Classification Group</a> -- <i>ISL,EE,Stanford (22th April 1996)</i><img src="new.gif">
<dd><a href="http://www.bonzi.com">Voice email</a> -- <i>Lossless audio compression (18th Feb 1996)</i>
<dd><a href="http://www.garlic.com/biz/eotek">Electro-Optical Technologies, Inc.</a> -- <i>Consultant (12th April 1996)</i> <img src="new.gif">
<dd><a href="http://quicktime.apple.com">quicktime.apple.com</a> -- <i>Quicktime site (11th Mar 1996)</i>
<dd><a href="http://www.atinternet.fr/image/">IMAGE etc.</a> -- <i>Commercial image compression software (Fractals/Wavelets) (29th Feb 1996)</i> <img src="new.gif">
<dd><a href="http://re7alpha.epm.ornl.gov/CcmDemo/">NCAR CCM Compression and Visualisation</a> -- <i>(18th Feb 1996)</i> <img src="new.gif">
<br>
<dd><a href="http://ipl.rpi.edu/SPIHT/">SPIHT</a> -- <I>Wavelet Natural Image Compressor (12th Feb 1996)</i> <img src="new.gif">
<br>
<dd><a href="http://info.lut.ac.uk/departments/el/research/sys/compression.html">Real-Time Lossless Compression Systems</a> -- <i>Loughborough University</i> <img src="new.gif">
<br>
<dd><a href="http://saigon.ece.wisc.edu/~waveweb/QMF.html">Web site for Multirate Signal Processing</a> -- <i>University of Wisconsin, Madison</i>
<br>
<dd><a href="http://quest.jpl.nasa.gov/Info-ZIP/">Info-ZIP</a> -- <i>free,
portable Zip and UnZip utilities</i>
<br>
<dd><a href="http://cesdis.gsfc.nasa.gov/">CEDIS</a> -- <i>NASA, Maryland, Image/data compression</i>
<br>
<dd><a href="http://www.c3.lanl.gov/~brislawn/ftp.html">CIC-3 Image Compression</a> -- <i>FBI Fingerprints</i>
<br>
<dd><a href="http://rainbow.ece.ucsb.edu/scl.html">Signal Compression Lab at UCSB</a>
<br>
<dd><a href="http://isdl.ee.washington.edu/COMPRESSION/homepage.html">University of
Washington compression lab</a> -- <I>VQ, Wavelets, Shlomo</i>
<br>
</dt>
<p>
<dt>
<i><b>Snippets</b></i>
<dd><a href="http://www-isl.stanford.edu/~gray/iii.html">Signal Processing
and the International Information Infrastructure</a> -- <i>Web sites (22th April 1996)</i> <img src="new.gif">
<dd><a href="http://www.scu.edu.au/ausweb95/papers/management/vanzyl/">Increasing Web bandwith</a> -- <i>comparing GIF, JPEG, Fractal compression</i>
<br>
<dd><a href="http://www.yahoo.com/Art/Computer_Generated/Fractals">Fractal Links on Yahoo</a>
<br>
<dd><a href="http://www.ddj.com/old/ddjw001d.htm">Digital Speech Compression GSM 06.10 RPE-LTP</a> -- <i>DDJ</i>
<br>
<dd><a href="http://www.crc.ricoh.com/CREW/">CREW</a> -- <i>Continuous tone loss(y/less) wavelet compression</i>
<br>
<dd><a href="ftp://ftp.csd.uwo.ca/pub/from_wu/">CALIC -- Context-based adaptive lossless image compressor</a>
<br>
<dd><a href="http://www-plateau.cs.berkeley.edu/mpeg/index.html">Berkeley MPEG</a> -- <i>MPEG tools</i>
<br>
<dd><a href="http://www.creative.net/~tristan/MPEG">MPEG Pointers and Resources</a>
<BR>
</dt>
<p>
<dt>
<i><b>Wavelets</b></i>
<dd>
<a href="http://www.cis.upenn.edu/~eero/epic.html">
EPIC (Efficient Pyramid Image Coder)</a> -- <i>by Eero Simoncelli</i>
<br>
<dd>
<a href="mailto://72234.2617@compuserve.com">Tucker, Michael</a> -- <i>FASTWAVE, audio/image compression</i>
<br>
<dd>
<a href="http://www.harc.edu">HARC</a> -- <i>Lossy Wavelet Compression technology</i>
<br>
<dd>
<a href="http://gabor.eecs.berkeley.edu/">UC Berkeley Wavelet Group</a>
<br>
<dd>
<a href="http://www.c3.lanl.gov/~cjhamil/Wavelets/main.html">Khoros Wavetlet and Compression Toolbox</a>
<br>
<dd>
<a href="http://arabigo.math.scarolina.edu:80/~wavelet/">The Wavelet Digest</a>
<br>
<dd>
<a href="http://jazz.rice.edu/publications.html">Rice DSP Publications Archive</a> -- <i>Wavelets, Time Frequency/Scale</i>
<br>
</dt>
<p>
<dt>
<i><b>Fractals</b></i>
<dd>
<a href="http://inls.ucsd.edu/y/Fractals/">Fractal Image Compression</a> -- <i>Software, Pointers, Conferences</i>
<br>
<dd>
<a href="http://www.fractal.com/">Fractal Design Corporation</a>
<br>
<dd>
<a href="http://www.webcom.com/~verrando/university/ifs.html">New Fractal Image Compression program</a>
<br>
</dt>
<p>
<dt>
<i><b>Companies</b></i>
<dd><a href="http://www.chips.ibm.com/products/aldc/index.html">IBM Hardware</a> -- <i>Compression chips (11th Mar 1996)</i> <img src="new.gif">
<dd><a href="http://www.summus.com">Summus Wavelet Technology</a> -- <i>Wavelet image and video compressors (11th Mar 1996)</i> <img src="new.gif">
<dd><a href="http://www.mitsubishi.co.jp/jp/fractal/fractal.html">Fractal Image Compression</a> -- <I>Mitsubishi (18th Feb 1996)</i> <img src="new.gif">
<dd><a href="http://www.terran-int.com/">Terran Interactive</a> -- <i>Video compression for the Mac (1st Feb 1996)</i> <img src="new.gif">
<br>
<dd><a href="http://darvision.kaist.ac.kr/dvmpeg.html">DV Mpeg</a> -- <i>Windows drivers (18th Feb 1996)</I> <img src="new.gif">
<br>
<dd><a href="http://www.crawford.com/cs/welcome.html">Crawford Compression Services</a> -- <i>MPEG post-production (18th Feb 1996)</i> <img src="new.gif">
<br>
<dd><a href="http://www.shore.net/~ict">Intelligent Compression Technologies</a> <i>(18th Feb 1996)</i> <img src="new.gif">
<br>
<dd><a href="http://www.jpg.com">Pegasus Imaging</a> -- <i>Compression software/dev. kits (12th Feb 1996)</i> <img src="new.gif">
<br>
<dd><a href="http://www.aladdinsys.com/">Aladdin Systems</a> -- <i>StuffIt compression products</i>
<br>
<dd><a href="http://www.stac.com/">Stac Electronics</a>
<br>
<dd><a href="http://www.ccinet.ab.ca/dcp.html">DCP Research</a> -- <i>Hardware solutions</i>
<br>
<dd><a href="http://www.aware.com/product_info/compression_overview.html">Aware Inc.</a> -- <i>Specialised compression company</i>
<br>
<dd><a href="http://www.inria.fr/rodeo/ivs.html">IVS - INRIA Videoconferencing System</a>
<br>
<dd><a href="http://sp25.ianus.cineca.it/telvox/telvoxen.htm">Telvox Teleinformatica</a> -- <i>Multiplatform Data Compressor</i>
<br>
<dd><a href="http://www.compression.com">Compression Technologies,
Inc</a> -- <i>Canada</i>
<br>
<dd><a href="http://www.optivision.com/">Optivision</a> -- <i>MPEG</i>
<br>
<dd><a href="http://www.pkware.com/">PKWARE</a> -- <i>Makers of PKZIP</i>
<br>
<dd><a href="http://www.infoanalytic.com/tayson/index.html">Multimedia Imaging Services</a>
<br>
</dt>
<p>
<dt>
<i><b>Audio compression</b></i>
<dd><a href="http://www.iis.fhg.de/departs/amm/index.html"> Fraunhofer Institut für Integrierte Schaltungen</a>
<br>
<dd><a href="http://svr-www.eng.cam.ac.uk/~ajr/speechCoding.html">Shorten</a>
<br>
<dd><a href="http://www.ddj.com/old/ddjw001d.htm">digital speech compression</a>
<br>
<dd><a href="http://www.cs.tu-berlin.de/~phade/audiowww.html">Audio compression references</a>
<br>
<dd><a href="http://www.fourmilab.ch/speakfree/windows/doc/compress.html">SpeakFreely - compression</a>
<br>
<dd><a href="http://www.fourmilab.ch/speakfree/windows/speak_freely.html#contents">SpeakFreely - Contents</a>
<br>

View File

@@ -0,0 +1,431 @@
#ifndef lint
static char Rcs_Id[] =
"$Id: fields.c,v 1.7 1994/01/06 05:26:37 geoff Exp $";
#endif
/*
* $Log: fields.c,v $
* Revision 1.7 1994/01/06 05:26:37 geoff
* Get rid of all references to System V string routines, for portability
* (sigh).
*
* Revision 1.6 1994/01/05 20:13:43 geoff
* Add the maxf parameter
*
* Revision 1.5 1994/01/04 02:40:21 geoff
* Make the increments settable (field_line_inc and field_field_inc).
* Add support for the FLD_NOSHRINK flag.
*
* Revision 1.4 1993/09/27 17:48:02 geoff
* Fix some lint complaints and some parenthesization errors.
*
* Revision 1.3 1993/09/09 01:11:11 geoff
* Add a return value to fieldwrite. Add support for backquotes and for
* unstripped backslashes.
*
* Revision 1.2 1993/08/26 00:02:50 geoff
* Fix a stupid null-pointer bug
*
* Revision 1.1 1993/08/25 21:32:05 geoff
* Initial revision
*
*/
#include <stdio.h>
#include "config.h"
#include "fields.h"
field_t * fieldread P ((FILE * file, char * delims,
int flags, int maxf));
/* Read a line with fields from a file */
field_t * fieldmake P ((char * line, int allocated, char * delims,
int flags, int maxf));
/* Make a field structure from a line */
static field_t * fieldparse P ((field_t * fieldp, char * line, char * delims,
int flags, int maxf));
/* Parse the fields in a line */
static int fieldbackch P ((char * str, char ** out, int strip));
/* Process backslash sequences */
int fieldwrite P ((FILE * file, field_t * fieldp, int delim));
/* Write a line with fields to a file */
void fieldfree P ((field_t * fieldp));
/* Free a field returned by fieldread */
unsigned int field_field_inc = 20; /* Increment to increase # fields by */
unsigned int field_line_inc = 512; /* Incr to increase line length by */
#ifndef USG
#define strchr index
#endif /* USG */
extern void free ();
extern char * malloc ();
extern char * realloc ();
extern char * strchr ();
extern int strlen ();
/*
* Read one line of the given file into a buffer, break it up into
* fields, and return them to the caller. The field_t structure
* returned must eventually be freed with fieldfree.
*/
field_t * fieldread (file, delims, flags, maxf)
FILE * file; /* File to read lines from */
char * delims; /* Characters to use for field delimiters */
int flags; /* Option flags; see fields.h */
int maxf; /* Maximum number of fields to parse */
{
register char * linebuf; /* Buffer to hold the line read in */
int linemax; /* Maximum line buffer size */
int linesize; /* Current line buffer size */
linebuf = (char *) malloc (field_line_inc);
if (linebuf == NULL)
return NULL;
linemax = field_line_inc;
linesize = 0;
/*
* Read in the line.
*/
while (fgets (&linebuf[linesize], linemax - linesize, file)
!= NULL)
{
linesize += strlen (&linebuf[linesize]);
if (linebuf[linesize - 1] == '\n')
break;
else
{
linemax += field_line_inc;
linebuf = (char *) realloc (linebuf, linemax);
if (linebuf == NULL)
return NULL;
}
}
if (linesize == 0)
{
free (linebuf);
return NULL;
}
return fieldmake (linebuf, 1, delims, flags, maxf);
}
field_t * fieldmake (line, allocated, delims, flags, maxf)
char * line; /* Line to make into a field structure */
int allocated; /* NZ if line allocated with malloc */
char * delims; /* Characters to use for field delimiters */
int flags; /* Option flags; see fields.h */
int maxf; /* Maximum number of fields to parse */
{
register field_t * fieldp; /* Structure describing the fields */
int linesize; /* Current line buffer size */
fieldp = (field_t *) malloc (sizeof (field_t));
if (fieldp == NULL)
return NULL;
fieldp->nfields = 0;
fieldp->linebuf = allocated ? line : NULL;
fieldp->fields = NULL;
fieldp->hadnl = 0;
linesize = strlen (line);
if (line[linesize - 1] == '\n')
{
line[--linesize] = '\0';
fieldp->hadnl = 1;
}
/*
* Shrink the line buffer if necessary.
*/
if (allocated && (flags & FLD_NOSHRINK) == 0)
{
line = fieldp->linebuf =
(char *) realloc (fieldp->linebuf, linesize + 1);
if (fieldp->linebuf == NULL)
{
fieldfree (fieldp);
return NULL;
}
}
return fieldparse (fieldp, line, delims, flags, maxf);
}
static field_t * fieldparse (fieldp, line, delims, flags, maxf)
register field_t * fieldp; /* Field structure to parse into */
register char * line; /* Line to be parsed */
char * delims; /* Characters to use for field delimiters */
int flags; /* Option flags; see fields.h */
int maxf; /* Maximum number of fields to parse */
{
int fieldmax; /* Max size of fields array */
char * lineout; /* Where to store xlated char in line */
char quote; /* Quote character in use */
fieldp->nfields = 0;
fieldmax =
(maxf != 0 && maxf < field_field_inc) ? maxf + 2 : field_field_inc;
fieldp->fields = (char **) malloc (fieldmax * sizeof (char *));
if (fieldp->fields == NULL)
{
fieldfree (fieldp);
return NULL;
}
if ((flags
& (FLD_SHQUOTES | FLD_SNGLQUOTES | FLD_BACKQUOTES | FLD_DBLQUOTES))
== FLD_SHQUOTES)
flags |= FLD_SNGLQUOTES | FLD_BACKQUOTES | FLD_DBLQUOTES;
while (1)
{
if (flags & FLD_RUNS)
{
while (*line != '\0' && strchr (delims, *line) != NULL)
line++; /* Skip runs of delimiters */
if (*line == '\0')
break;
}
fieldp->fields[fieldp->nfields] = lineout = line;
/*
* Skip to the next delimiter. At the end of skipping, "line" will
* point to either a delimiter or a null byte.
*/
if (flags
& (FLD_SHQUOTES | FLD_SNGLQUOTES | FLD_BACKQUOTES
| FLD_DBLQUOTES | FLD_BACKSLASH))
{
while (*line != '\0')
{
if (strchr (delims, *line) != NULL)
break;
else if (((flags & FLD_SNGLQUOTES) && *line == '\'')
|| ((flags & FLD_BACKQUOTES) && *line == '`')
|| ((flags & FLD_DBLQUOTES) && *line == '"'))
{
if ((flags & FLD_SHQUOTES) == 0
&& line != fieldp->fields[fieldp->nfields])
quote = '\0';
else
quote = *line;
}
else
quote = '\0';
if (quote == '\0')
{
if (*line == '\\' && (flags & FLD_BACKSLASH))
{
line++;
if (*line == '\0')
break;
line += fieldbackch (line, &lineout,
flags & FLD_STRIPQUOTES);
}
else
*lineout++ = *line++;
}
else
{
/* Process quoted string */
if ((flags & FLD_STRIPQUOTES) == 0)
*lineout++ = quote;
++line;
while (*line != '\0')
{
if (*line == quote)
{
if ((flags & FLD_STRIPQUOTES) == 0)
*lineout++ = quote;
line++; /* Go on past quote */
if ((flags & FLD_SHQUOTES) == 0)
{
while (*line != '\0'
&& strchr (delims, *line) == NULL)
line++; /* Skip to delimiter */
}
break;
}
else if (*line == '\\')
{
if (flags & FLD_BACKSLASH)
{
line++;
if (*line == '\0')
break;
else
line += fieldbackch (line, &lineout,
flags & FLD_STRIPQUOTES);
}
else
{
*lineout++ = '\\';
if (*++line == '\0')
break;
*lineout++ = *line;
}
}
else
*lineout++ = *line++;
}
}
}
}
else
{
while (*line != '\0' && strchr (delims, *line) == NULL)
line++; /* Skip to delimiter */
lineout = line;
}
fieldp->nfields++;
if (*line++ == '\0')
break;
if (maxf != 0 && fieldp->nfields > maxf)
break;
*lineout = '\0';
if (fieldp->nfields >= fieldmax)
{
fieldmax += field_field_inc;
fieldp->fields =
(char **) realloc (fieldp->fields, fieldmax * sizeof (char *));
if (fieldp->fields == NULL)
{
fieldfree (fieldp);
return NULL;
}
}
}
/*
* Shrink the field pointers and return the field structure.
*/
if ((flags & FLD_NOSHRINK) == 0 && fieldp->nfields >= fieldmax)
{
fieldp->fields = (char **) realloc (fieldp->fields,
(fieldp->nfields + 1) * sizeof (char *));
if (fieldp->fields == NULL)
{
fieldfree (fieldp);
return NULL;
}
}
fieldp->fields[fieldp->nfields] = NULL;
return fieldp;
}
static int fieldbackch (str, out, strip)
register char * str; /* First char of backslash sequence */
register char ** out; /* Where to store result */
int strip; /* NZ to convert the sequence */
{
register int ch; /* Character being developed */
char * origstr; /* Original value of str */
if (!strip)
{
*(*out)++ = '\\';
if (*str != 'x' && *str != 'X' && (*str < '0' || *str > '7'))
{
*(*out)++ = *str;
return *str != '\0';
}
}
switch (*str)
{
case '\0':
*(*out)++ = '\0';
return 0;
case 'a':
*(*out)++ = '\007';
return 1;
case 'b':
*(*out)++ = '\b';
return 1;
case 'f':
*(*out)++ = '\f';
return 1;
case 'n':
*(*out)++ = '\n';
return 1;
case 'r':
*(*out)++ = '\r';
return 1;
case 'v':
*(*out)++ = '\v';
return 1;
case 'X':
case 'x':
/* Hexadecimal sequence */
origstr = str++;
ch = 0;
if (*str >= '0' && *str <= '9')
ch = *str++ - '0';
else if (*str >= 'a' && *str <= 'f')
ch = *str++ - 'a' + 0xa;
else if (*str >= 'A' && *str <= 'F')
ch = *str++ - 'A' + 0xa;
if (*str >= '0' && *str <= '9')
ch = (ch << 4) | (*str++ - '0');
else if (*str >= 'a' && *str <= 'f')
ch = (ch << 4) | (*str++ - 'a' + 0xa);
else if (*str >= 'A' && *str <= 'F')
ch = (ch << 4) | (*str++ - 'A' + 0xa);
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
/* Octal sequence */
origstr = str;
ch = *str++ - '0';
if (*str >= '0' && *str <= '7')
ch = (ch << 3) | (*str++ - '0');
if (*str >= '0' && *str <= '7')
ch = (ch << 3) | (*str++ - '0');
break;
default:
*(*out)++ = *str;
return 1;
}
if (strip)
{
*(*out)++ = ch;
return str - origstr;
}
else
{
for (ch = 0; origstr < str; ch++)
*(*out)++ = *origstr++;
return ch;
}
}
int fieldwrite (file, fieldp, delim)
FILE * file; /* File to write to */
register field_t * fieldp; /* Field structure to write */
int delim; /* Delimiter to place between fields */
{
int error; /* NZ if an error occurs */
register int fieldno; /* Number of field being written */
error = 0;
for (fieldno = 0; fieldno < fieldp->nfields; fieldno++)
{
if (fieldno != 0)
error |= putc (delim, file) == EOF;
error |= fputs (fieldp->fields[fieldno], file) == EOF;
}
if (fieldp->hadnl)
error |= putc ('\n', file) == EOF;
return error;
}
void fieldfree (fieldp)
register field_t * fieldp; /* Field structure to free */
{
if (fieldp == NULL)
return;
if (fieldp->linebuf != NULL)
free ((char *) fieldp->linebuf);
if (fieldp->fields != NULL)
free ((char *) fieldp->fields);
free ((char *) fieldp);
}

Binary file not shown.

View File

@@ -0,0 +1,94 @@
;;; -*- Mode: Lisp; Syntax: Common-Lisp; -*-
(define-language
:grammar
'(((S $any) -> (S1 $any))
((S (Compound $s1 $s2)) -> (S1 $s1) (Conjunction) (S1 $s2))
((S1 (Statement $v)) -> (NP $subj) (VP $subj $tense $v))
((S1 (Acknowledge $a)) -> (Acknowledge $a))
((S1 (Command $v)) -> (VP Self present $v))
((S1 (Question $v)) -> (Aux $tense) (NP $subj) (VP $subj $tense $v))
((S1 (Question $v)) -> (Be $tense) (NP $subj) (Be-Arg $subj $tense $v))
((Be-Arg $subj $tense (Occur $tense (loc $subj $loc))) ->
(Loc-Adjunct $tense (loc $subj $loc)))
((VP $subj $tense (Occur $tense $v)) -> (VP1 $subj $tense $v))
((VP $subj $tense (Occur $tense $v)) -> (Aux $tense)(VP1 $subj present $v))
((VP1 $subj $tense $v) -> (VP2 $subj $tense $v) (Adjunct? $v))
((VP2 $subj $tense ($rel $subj $loc)) ->
(Verb/in $rel $tense))
((VP2 $subj $tense ($rel $subj $loc $obj)) ->
(Verb/tr $rel $tense) (NP $obj))
((VP2 $subj $tense ($rel $subj $loc $obj $obj2)) ->
(Verb/di $rel $tense) (NP $obj) (NP $obj2))
((VP2 $subj $tense (loc $subj $loc)) ->
(Be $tense) (Loc-Adjunct $tense (loc $subj $loc)))
((NP $n) -> (Pronoun $n))
((NP $n) -> (Article) (Noun $n))
((NP $n) -> (Noun $n))
((NP ($x $y)) -> (Number $x) (Number $y))
((PP ($prep $n)) -> (Prep $prep) (NP $n))
((Adjunct? $v) ->)
((Adjunct? $v) -> (Loc-Adjunct $tense $v))
#+Allegro ((Loc-Adjunct $tense ($rel $subj $loc @rest)) -> (PP $loc))
#+Allegro ((Loc-Adjunct $tense ($rel $subj $loc @rest)) -> (Adjunct $loc))
#+Lucid ((Loc-Adjunct $tense ($rel $subj $loc . $rest)) -> (PP $loc))
#+Lucid ((Loc-Adjunct $tense ($rel $subj $loc . $rest)) -> (Adjunct $loc))
)
:lexicon
'(
((Acknowledge $a) -> (yes true) (no false) (maybe unknown) (huh unparsed))
((Adjunct $loc) -> here there (nearby near) near left right up down)
((Article) -> a an the)
((Aux $tense) -> (will future) (did past) (do $finite))
((Be $tense) -> (am present) (are present) (is present) (be $finite)
(was past) (were past))
((Conjunction) -> and --)
((Noun $n) -> gold Wumpus pit breeze stench glitter nothing)
((Number $n) -> 0 1 2 3 4 5 6 7 8 9)
((Prep $prep) -> in at to near)
((Pronoun $n) -> (you self) (me master) (I master))
((Verb/in $rel $tense) -> (go move $finite) (went move past)
(move move $finite) (move move past) (shoot shoot $finite))
((Verb/tr $rel $tense) -> (move carry $finite) (moved carry past)
(carry carry $finite) (carry carried past)
(grab grab $finite) (grab grabbed past) (get grab $finite)
(got grab past) (release release $finite) (release release past)
(drop release $finite) (dropped release past) (shoot shoot-at $finite)
(shot shoot-at past) (kill shoot-at $finite) (killed shoot-at past)
(smell perceive $finite) (feel perceive $finite) (felt perceive past))
((Verb/di $rel $tense) -> (bring bring $finite) (brought bring past)
(get bring $finite) (got bring past))
))
(defparameter *sentences*
'((I will shoot the wumpus at 4 4)
(yes)
(You went right -- I will go left)
(carry the gold)
(yes and no)
(did you bring me the gold)
(a breeze is here -- I am near 5 3)
(a stench is in 3 5)
(a pit is nearby)
(is the wumpus near)
(Did you go to 3 8)
(Yes -- Nothing is there)
(Shoot -- Shoot left)
(Kill the wumpus -- shoot up)))
(defun ss (&optional (sentences *sentences*))
"Run some test sentences, and count how many were not parsed."
(count-if-not
#'(lambda (s)
(format t "~2&>>> ~(~{~a ~}~)~%" s)
(write (second (parse s)) :pretty t))
*sentences*))

Some files were not shown because too many files have changed in this diff Show More