Merge commit '5edb23a7abbffb2ec7874d0352b993e1b4193374' into feature/update-dependencies

This commit is contained in:
Sergey Abramchuk
2019-06-17 09:44:01 +03:00
126 changed files with 42387 additions and 1287 deletions

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

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

@@ -285,6 +285,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.
@@ -292,6 +296,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.
@@ -402,12 +409,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
@@ -600,7 +611,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;

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

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)

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
)

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)

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

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

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

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

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

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;

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

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

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.

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

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