Squashed 'OpenVPN Adapter/Vendors/openvpn/' changes from e6d68831a..35bbca799

35bbca799 Merged in OVPN3-184-generate-warning (pull request #1)
a73d2ce68 Merged in antonio/OVPN3-169-pure-ssl-transport (pull request #3)
8d7f5f3c1 Merged in feature/docker (pull request #2)
d9b5055cd [OVPN3-169] cli.cpp: compile with -DOPENVPN_TLS_LINK when requested
2d99bbfea [OVPN3-169] cliopt.hpp: add support for TLS transport module
62c8461d2 [OVPN3-169] tcpcli.hpp: add runtime support for TLSLink
e0e76bb28 [OVPN3-169] tcplink: introduce LinkBase abstract class
a71014d40 [OVPN3-169] tcplink: create LinkCommon class and inherit from it
cfd6df5bc build system: fix 'git apply'
3e49de7de [OVPN3-210] ovpncli: handle "allow-name-constraints" for OpenSSL
08d72bd76 [OVPN3-184] mbedtls: handle Name Constraints
40c70113d [OVPN3-184] Add mbedTLS patch
ef8d11f34 [OVPN3-169] OpenSSL: implement write_ciphertext_unbuffered() function
37dc86378 [OVPN3-169] mbedTLS: implement write_ciphertext_unbuffered() function
5834ed401 [OVPN3-169] SSLAPI: add write_ciphertext_unbuffered() function
071050b5f vars-linux-dbg: update linux debug profile
5bbfe68c3 [OVPN3-169] Protocol: add support for TLS transport protocol type
dc12d3189 [OVPN3-223] build: add docker images

git-subtree-dir: OpenVPN Adapter/Vendors/openvpn
git-subtree-split: 35bbca799dfa3fbe8e17f8d6e94c3946c397b593
This commit is contained in:
Sergey Abramchuk
2018-05-03 11:46:13 +03:00
parent 84ad2a289f
commit 56284506fc
26 changed files with 1162 additions and 412 deletions
+24
View File
@@ -54,6 +54,7 @@ namespace openvpn {
PAUSE,
RESUME,
RELAY,
UNSUPPORTED_FEATURE,
// start of nonfatal errors, must be marked by NONFATAL_ERROR_START below
TRANSPORT_ERROR,
@@ -105,6 +106,7 @@ namespace openvpn {
"PAUSE",
"RESUME",
"RELAY",
"UNSUPPORTED_FEATURE",
// nonfatal errors
"TRANSPORT_ERROR",
@@ -253,6 +255,28 @@ namespace openvpn {
TLSVersionMinFail() : Base(TLS_VERSION_MIN) {}
};
struct UnsupportedFeature : public Base
{
typedef RCPtr<UnsupportedFeature> Ptr;
UnsupportedFeature(const std::string& name_arg, const std::string& reason_arg, bool critical_arg)
: Base(UNSUPPORTED_FEATURE),
name(name_arg),
reason(reason_arg),
critical(critical_arg) {}
std::string name;
std::string reason;
bool critical;
virtual std::string render() const
{
std::ostringstream out;
out << "name: " << name << ", reason: " << reason << ", critical: " << critical;
return out.str();
}
};
struct Connected : public Base
{
typedef RCPtr<Connected> Ptr;
+12 -2
View File
@@ -667,6 +667,9 @@ namespace openvpn {
if (relay_mode)
lflags |= SSLConfigAPI::LF_RELAY_MODE;
if (opt.exists("allow-name-constraints"))
lflags |= SSLConfigAPI::LF_ALLOW_NAME_CONSTRAINTS;
// client SSL config
SSLLib::SSLAPI::Config::Ptr cc(new SSLLib::SSLAPI::Config());
cc->set_external_pki_callback(config.external_pki);
@@ -730,7 +733,6 @@ namespace openvpn {
#endif
#else
if (dco)
{
DCO::TransportConfig transconf;
@@ -789,7 +791,11 @@ namespace openvpn {
#endif
transport_factory = udpconf;
}
else if (transport_protocol.is_tcp())
else if (transport_protocol.is_tcp()
#ifdef OPENVPN_TLS_LINK
|| transport_protocol.is_tls()
#endif
)
{
// TCP transport
TCPTransport::ClientConfig::Ptr tcpconf = TCPTransport::ClientConfig::new_obj();
@@ -797,6 +803,10 @@ namespace openvpn {
tcpconf->frame = frame;
tcpconf->stats = cli_stats;
tcpconf->socket_protect = socket_protect;
#ifdef OPENVPN_TLS_LINK
if (transport_protocol.is_tls())
tcpconf->use_tls = true;
#endif
#ifdef OPENVPN_GREMLIN
tcpconf->gremlin_config = gremlin_config;
#endif
+6 -1
View File
@@ -291,10 +291,15 @@ namespace openvpn {
protoConfig->load(options, ProtoContextOptions(), -1, false);
}
unsigned int lflags = SSLConfigAPI::LF_PARSE_MODE;
if (options.exists("allow-name-constraints"))
lflags |= SSLConfigAPI::LF_ALLOW_NAME_CONSTRAINTS;
// ssl lib configuration
try {
sslConfig.reset(new SSLLib::SSLAPI::Config());
sslConfig->load(options, SSLConfigAPI::LF_PARSE_MODE);
sslConfig->load(options, lflags);
} catch (...) {
sslConfig.reset();
}
+10 -4
View File
@@ -791,10 +791,16 @@ namespace openvpn {
uint32_t tls_warnings = get_tls_warnings();
if (tls_warnings & SSLAPI::TLS_WARN_SIG_MD5)
{
ClientEvent::Base::Ptr ev = new ClientEvent::Warn("TLS: received certificate signed with MD5. Please inform your admin to upgrade to a stronger algorithm. Support for MD5 will be dropped at end of Apr 2018");
cli_events->add_event(std::move(ev));
}
{
ClientEvent::Base::Ptr ev = new ClientEvent::Warn("TLS: received certificate signed with MD5. Please inform your admin to upgrade to a stronger algorithm. Support for MD5 will be dropped at end of Apr 2018");
cli_events->add_event(std::move(ev));
}
if (tls_warnings & SSLAPI::TLS_WARN_NAME_CONSTRAINTS)
{
ClientEvent::Base::Ptr ev = new ClientEvent::Warn("TLS: Your CA contains a 'x509v3 Name Constraints' extension, but its validation is not supported. This might be a security breach, please contact your administrator.");
cli_events->add_event(std::move(ev));
}
}
// base class calls here when primary session transitions to ACTIVE state
+5 -4
View File
@@ -130,12 +130,12 @@ namespace openvpn {
return chain;
}
~X509Cert()
virtual ~X509Cert()
{
dealloc();
}
private:
protected:
void alloc()
{
if (!chain)
@@ -145,6 +145,9 @@ namespace openvpn {
}
}
mbedtls_x509_crt *chain;
private:
void dealloc()
{
if (chain)
@@ -155,8 +158,6 @@ namespace openvpn {
}
}
mbedtls_x509_crt *chain;
static const std::string begin_cert;
static const std::string end_cert;
};
+42 -10
View File
@@ -35,6 +35,7 @@
#include <mbedtls/oid.h>
#include <mbedtls/sha1.h>
#include <mbedtls/debug.h>
#include <mbedtls/asn1.h>
#include <openvpn/common/size.hpp>
#include <openvpn/common/exception.hpp>
@@ -211,7 +212,8 @@ namespace openvpn {
tls_cert_profile(TLSCertProfile::UNDEF),
local_cert_enabled(true),
enable_renegotiation(false),
force_aes_cbc_ciphersuites(false) {}
force_aes_cbc_ciphersuites(false),
allow_name_constraints(false) {}
virtual SSLFactoryAPI::Ptr new_factory()
{
@@ -456,6 +458,8 @@ namespace openvpn {
&& opt.exists("client-cert-not-required"))
flags |= SSLConst::NO_VERIFY_PEER;
allow_name_constraints = lflags & LF_ALLOW_NAME_CONSTRAINTS;
// ca
{
std::string ca_txt = opt.cat("ca");
@@ -532,6 +536,16 @@ namespace openvpn {
}
}
bool name_constraints_allowed() const
{
return allow_name_constraints;
}
bool is_server() const
{
return mode.is_server();
}
private:
const mbedtls_x509_crt_profile *select_crt_profile() const
{
@@ -553,8 +567,12 @@ namespace openvpn {
}
Mode mode;
protected:
MbedTLSPKI::X509Cert::Ptr crt_chain; // local cert chain (including client cert + extra certs)
MbedTLSPKI::X509Cert::Ptr ca_chain; // CA chain for remote verification
private:
MbedTLSPKI::X509CRL::Ptr crl_chain; // CRL chain for remote verification
MbedTLSPKI::PKContext::Ptr priv_key; // private key
std::string priv_key_pwd; // private key password
@@ -573,6 +591,7 @@ namespace openvpn {
bool local_cert_enabled;
bool enable_renegotiation;
bool force_aes_cbc_ciphersuites;
bool allow_name_constraints;
RandomAPI::Ptr rng; // random data source
};
@@ -649,6 +668,14 @@ namespace openvpn {
overflow = true;
}
virtual void write_ciphertext_unbuffered(const unsigned char *data, const size_t size)
{
if (ct_in.size() < MAX_CIPHERTEXT_IN)
ct_in.write(data, size);
else
overflow = true;
}
virtual bool read_ciphertext_ready() const
{
return !ct_out.empty();
@@ -676,12 +703,12 @@ namespace openvpn {
return authcert;
}
~SSL()
virtual ~SSL()
{
erase();
}
private:
protected:
SSL(MbedTLSContext* ctx, const char *hostname)
{
clear();
@@ -863,6 +890,10 @@ namespace openvpn {
}
}
mbedtls_ssl_config *sslconf; // SSL configuration parameters for SSL connection object
MbedTLSContext *parent;
private:
// cleartext read callback
static int ct_read_func(void *arg, unsigned char *data, size_t length)
{
@@ -925,9 +956,7 @@ namespace openvpn {
clear();
}
MbedTLSContext *parent;
mbedtls_ssl_context *ssl; // underlying SSL connection object
mbedtls_ssl_config *sslconf; // SSL configuration parameters for SSL connection object
MbedTLSPKI::PKContext epki_ctx; // external PKI context
RandomAPI::Ptr rng; // random data source
MemQStream ct_in; // write ciphertext to here
@@ -954,13 +983,13 @@ namespace openvpn {
{
return config->mode;
}
~MbedTLSContext()
virtual ~MbedTLSContext()
{
erase();
}
private:
protected:
MbedTLSContext(Config* config_arg)
: config(config_arg)
{
@@ -972,6 +1001,7 @@ namespace openvpn {
}
}
private:
size_t key_len() const
{
return mbedtls_pk_get_bitlen(&config->crt_chain->get()->pk) / 8;
@@ -1114,6 +1144,7 @@ namespace openvpn {
return os.str();
}
protected:
static int verify_callback_client(void *arg, mbedtls_x509_crt *cert, int depth, uint32_t *flags)
{
MbedTLSContext::SSL *ssl = (MbedTLSContext::SSL *)arg;
@@ -1231,6 +1262,9 @@ namespace openvpn {
return 0;
}
Config::Ptr config;
private:
static std::string cert_info(const mbedtls_x509_crt *cert, const char *prefix = nullptr)
{
const size_t buf_size = 4096;
@@ -1354,8 +1388,6 @@ namespace openvpn {
MbedTLSContext *self = (MbedTLSContext *) arg;
return self->key_len();
}
Config::Ptr config;
};
} // namespace openvpn
+9
View File
@@ -469,6 +469,15 @@ namespace openvpn {
overflow = true;
}
virtual void write_ciphertext_unbuffered(const unsigned char *data, const size_t size)
{
bmq_stream::MemQ* in = bmq_stream::memq_from_bio(ct_in);
if (in->size() < MAX_CIPHERTEXT_IN)
in->write(data, size);
else
overflow = true;
}
virtual bool read_ciphertext_ready() const
{
return !bmq_stream::memq_from_bio(ct_out)->empty();
+5 -2
View File
@@ -51,6 +51,7 @@ namespace openvpn {
enum TLSWarnings {
TLS_WARN_SIG_MD5 = (1 << 0),
TLS_WARN_NAME_CONSTRAINTS = (1 << 1)
};
typedef RCPtr<SSLAPI> Ptr;
@@ -60,6 +61,7 @@ namespace openvpn {
virtual ssize_t read_cleartext(void *data, const size_t capacity) = 0;
virtual bool read_cleartext_ready() const = 0;
virtual void write_ciphertext(const BufferPtr& buf) = 0;
virtual void write_ciphertext_unbuffered(const unsigned char *data, const size_t size) = 0;
virtual bool read_ciphertext_ready() const = 0;
virtual BufferPtr read_ciphertext() = 0;
virtual std::string ssl_handshake_details() const = 0;
@@ -109,9 +111,10 @@ namespace openvpn {
};
enum LoadFlags {
LF_PARSE_MODE = (1<<0),
LF_PARSE_MODE = (1<<0),
LF_ALLOW_CLIENT_CERT_NOT_REQUIRED = (1<<1),
LF_RELAY_MODE = (1<<2), // look for "relay-ca" instead of "ca" directive
LF_RELAY_MODE = (1<<2), // look for "relay-ca" instead of "ca" directive
LF_ALLOW_NAME_CONSTRAINTS = (1<<3) // do not fail on Name Constraints ext and drop a warning to UI
};
std::string private_key_type_string() const
+8 -1
View File
@@ -40,6 +40,9 @@
#include <openvpn/mbedtls/crypto/api.hpp>
#include <openvpn/mbedtls/ssl/sslctx.hpp>
#include <openvpn/mbedtls/util/rand.hpp>
#ifdef HAVE_OPENVPN_COMMON
#include <openvpn/mbedtls/ssl/sslctxnc.hpp>
#endif
#ifdef OPENVPN_PLATFORM_UWP
#include <openvpn/mbedtls/util/uwprand.hpp>
#endif
@@ -56,7 +59,11 @@ namespace openvpn {
#if defined(USE_MBEDTLS)
#define SSL_LIB_NAME "MbedTLS"
typedef MbedTLSCryptoAPI CryptoAPI;
typedef MbedTLSContext SSLAPI;
#ifdef HAVE_OPENVPN_COMMON
typedef MbedTLSContextNameConstraints SSLAPI;
#else
typedef MbedTLSContext SSLAPI;
#endif
#if defined OPENVPN_PLATFORM_UWP
typedef MbedTLSRandomWithUWPEntropy RandomAPI;
#else
+1 -1
View File
@@ -216,7 +216,7 @@ namespace openvpn {
typedef TCPTransport::Link<openvpn_io::ip::tcp, Client*, false> LinkImpl;
friend class ClientConfig; // calls constructor
friend LinkImpl; // calls tcp_read_handler
friend LinkImpl::Base; // calls tcp_read_handler
public:
virtual void transport_start()
+46 -13
View File
@@ -29,6 +29,9 @@
#include <openvpn/io/io.hpp>
#include <openvpn/transport/tcplink.hpp>
#ifdef OPENVPN_TLS_LINK
#include <openvpn/transport/tlslink.hpp>
#endif
#include <openvpn/transport/client/transbase.hpp>
#include <openvpn/transport/socket_protect.hpp>
#include <openvpn/client/remotelist.hpp>
@@ -48,6 +51,10 @@ namespace openvpn {
SocketProtect* socket_protect;
#ifdef OPENVPN_TLS_LINK
bool use_tls = false;
#endif
#ifdef OPENVPN_GREMLIN
Gremlin::Config::Ptr gremlin_config;
#endif
@@ -72,9 +79,12 @@ namespace openvpn {
typedef RCPtr<Client> Ptr;
typedef Link<openvpn_io::ip::tcp, Client*, false> LinkImpl;
#ifdef OPENVPN_TLS_LINK
typedef TLSLink<openvpn_io::ip::tcp, Client*, false> LinkImplTLS;
#endif
friend class ClientConfig; // calls constructor
friend LinkImpl; // calls tcp_read_handler
friend LinkImpl::Base; // calls tcp_read_handler
public:
virtual void transport_start()
@@ -207,24 +217,24 @@ namespace openvpn {
return false;
}
void tcp_eof_handler() // called by LinkImpl
void tcp_eof_handler() // called by LinkImpl::Base
{
config->stats->error(Error::NETWORK_EOF_ERROR);
tcp_error_handler("NETWORK_EOF_ERROR");
}
bool tcp_read_handler(BufferAllocated& buf) // called by LinkImpl
bool tcp_read_handler(BufferAllocated& buf) // called by LinkImpl::Base
{
parent->transport_recv(buf);
return !stop_requeueing;
}
void tcp_write_queue_needs_send() // called by LinkImpl
void tcp_write_queue_needs_send() // called by LinkImpl::Base
{
parent->transport_needs_send();
}
void tcp_error_handler(const char *error) // called by LinkImpl
void tcp_error_handler(const char *error) // called by LinkImpl::Base
{
std::ostringstream os;
os << "Transport error on '" << server_host << ": " << error;
@@ -302,12 +312,35 @@ namespace openvpn {
{
if (!error)
{
impl.reset(new LinkImpl(this,
socket,
0, // // send_queue_max_size is unlimited because we regulate size in cliproto.hpp
config->free_list_max_size,
(*config->frame)[Frame::READ_LINK_TCP],
config->stats));
#ifdef OPENVPN_TLS_LINK
if (config->use_tls)
{
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_local_cert_enabled(false);
ssl_conf->set_frame(config->frame);
ssl_conf->set_rng(new SSLLib::RandomAPI(false));
impl.reset(new LinkImplTLS(this,
io_context,
socket,
0,
config->free_list_max_size,
config->frame,
config->stats,
ssl_conf->new_factory()));
}
else
#endif
impl.reset(new LinkImpl(this,
socket,
0, // send_queue_max_size is unlimited because we regulate size in cliproto.hpp
config->free_list_max_size,
(*config->frame)[Frame::READ_LINK_TCP],
config->stats));
#ifdef OPENVPN_GREMLIN
impl->gremlin_config(config->gremlin_config);
#endif
@@ -334,9 +367,9 @@ namespace openvpn {
openvpn_io::ip::tcp::socket socket;
ClientConfig::Ptr config;
TransportClientParent* parent;
LinkImpl::Ptr impl;
LinkBase::Ptr impl;
openvpn_io::ip::tcp::resolver resolver;
LinkImpl::protocol::endpoint server_endpoint;
LinkImpl::Base::protocol::endpoint server_endpoint;
bool halt;
bool stop_requeueing;
};
+30 -3
View File
@@ -41,11 +41,14 @@ namespace openvpn {
TCPv4,
UDPv6,
TCPv6,
TLSv4, // TLS over IPv4
TLSv6, // TLS over IPv6
UnixStream, // unix domain socket (stream)
UnixDGram, // unix domain socket (datagram)
NamedPipe, // named pipe (Windows only)
UDP=UDPv4,
TCP=TCPv4,
TLS=TLSv4,
};
enum AllowSuffix {
@@ -64,8 +67,9 @@ namespace openvpn {
bool is_udp() const { return type_ == UDPv4 || type_ == UDPv6; }
bool is_tcp() const { return type_ == TCPv4 || type_ == TCPv6; }
bool is_reliable() const { return is_tcp(); }
bool is_ipv6() const { return type_ == UDPv6 || type_ == TCPv6; }
bool is_tls() const { return type_ == TLSv4 || type_ == TLSv6; }
bool is_reliable() const { return is_tcp() || is_tls(); }
bool is_ipv6() const { return type_ == UDPv6 || type_ == TCPv6 || type_ == TLSv6; }
bool is_unix() const { return type_ == UnixStream || type_ == UnixDGram; }
bool is_named_pipe() const { return type_ == NamedPipe; }
bool is_local() const { return is_unix() || is_named_pipe(); }
@@ -87,7 +91,7 @@ namespace openvpn {
unsigned int extra_transport_bytes() const
{
return is_tcp() ? sizeof(std::uint16_t) : 0;
return (is_tcp() || is_tls()) ? sizeof(std::uint16_t) : 0;
}
void mod_addr_version(const IP::Addr& addr)
@@ -101,12 +105,16 @@ namespace openvpn {
type_ = UDPv4;
else if (is_tcp())
type_ = TCPv4;
else if (is_tls())
type_ = TLSv4;
break;
case IP::Addr::V6:
if (is_udp())
type_ = UDPv6;
else if (is_tcp())
type_ = TCPv6;
else if (is_tls())
type_ = TLSv6;
break;
}
}
@@ -157,6 +165,9 @@ namespace openvpn {
return 3;
case NamedPipe:
return 4;
case TLSv4:
case TLSv6:
return 5;
default:
return -1;
}
@@ -174,6 +185,10 @@ namespace openvpn {
return "UDPv6";
case TCPv6:
return "TCPv6";
case TLSv4:
return "TLSv4";
case TLSv6:
return "TLSv6";
case UnixStream:
return "UnixStream";
case UnixDGram:
@@ -199,6 +214,10 @@ namespace openvpn {
return "udp6";
case TCPv6:
return "tcp6";
case TLSv4:
return "tls4";
case TLSv6:
return "tls6";
case UnixStream:
return "unix-stream";
case UnixDGram:
@@ -224,6 +243,10 @@ namespace openvpn {
return force_ipv4 ? "UDPv4" : "UDPv6";
case TCPv6:
return force_ipv4 ? "TCPv4_CLIENT" : "TCPv6_CLIENT";
case TLSv4:
return "TLSv4";
case TLSv6:
return force_ipv4 ? "TLSv4" : "TLSv6";
default:
return "UNDEF_PROTO";
}
@@ -268,6 +291,8 @@ namespace openvpn {
ret = UDPv4;
else if (s1 == "tcp")
ret = TCPv4;
else if (s1 == "tls")
ret = TLSv4;
}
else if (s2 == "6" || s2 == "v6")
{
@@ -275,6 +300,8 @@ namespace openvpn {
ret = UDPv6;
else if (s1 == "tcp")
ret = TCPv6;
else if (s1 == "tls")
ret = TLSv6;
}
}
return ret;
+24 -356
View File
@@ -37,401 +37,69 @@
#include <openvpn/log/sessionstats.hpp>
#include <openvpn/transport/pktstream.hpp>
#include <openvpn/transport/mutate.hpp>
#include <openvpn/transport/tcplinkcommon.hpp>
#ifdef OPENVPN_GREMLIN
#include <openvpn/transport/gremlin.hpp>
#endif
#if defined(OPENVPN_DEBUG_TCPLINK) && OPENVPN_DEBUG_TCPLINK >= 1
#define OPENVPN_LOG_TCPLINK_ERROR(x) OPENVPN_LOG(x)
#else
#define OPENVPN_LOG_TCPLINK_ERROR(x)
#endif
#if defined(OPENVPN_DEBUG_TCPLINK) && OPENVPN_DEBUG_TCPLINK >= 3
#define OPENVPN_LOG_TCPLINK_VERBOSE(x) OPENVPN_LOG(x)
#else
#define OPENVPN_LOG_TCPLINK_VERBOSE(x)
#endif
namespace openvpn {
namespace TCPTransport {
struct PacketFrom
{
typedef std::unique_ptr<PacketFrom> SPtr;
BufferAllocated buf;
};
template <typename Protocol, typename ReadHandler, bool RAW_MODE_ONLY>
class Link : public RC<thread_unsafe_refcount>
class Link : public LinkCommon<Protocol,
ReadHandler,
RAW_MODE_ONLY>
{
typedef std::deque<BufferPtr> Queue;
public:
typedef LinkCommon<Protocol,
ReadHandler,
RAW_MODE_ONLY> Base;
typedef RCPtr<Link> Ptr;
typedef Protocol protocol;
friend Base;
Link(ReadHandler read_handler_arg,
typename Protocol::socket& socket_arg,
const size_t send_queue_max_size_arg, // 0 to disable
const size_t free_list_max_size_arg,
const Frame::Context& frame_context_arg,
const SessionStats::Ptr& stats_arg)
: socket(socket_arg),
halt(false),
read_handler(read_handler_arg),
frame_context(frame_context_arg),
stats(stats_arg),
send_queue_max_size(send_queue_max_size_arg),
free_list_max_size(free_list_max_size_arg)
{
set_raw_mode(false);
}
#ifdef OPENVPN_GREMLIN
void gremlin_config(const Gremlin::Config::Ptr& config)
{
if (config)
gremlin.reset(new Gremlin::SendRecvQueue(socket.get_executor().context(), config, true));
}
#endif
// In raw mode, data is sent and received without any special encapsulation.
// In non-raw mode, data is packetized by prepending a 16-bit length word
// onto each packet. The OpenVPN protocol runs in non-raw mode, while other
// TCP protocols such as HTTP or HTTPS would run in raw mode.
// This method is a no-op if RAW_MODE_ONLY is true.
void set_raw_mode(const bool mode)
{
set_raw_mode_read(mode);
set_raw_mode_write(mode);
}
void set_raw_mode_read(const bool mode)
{
if (RAW_MODE_ONLY)
raw_mode_read = true;
else
raw_mode_read = mode;
}
void set_raw_mode_write(const bool mode)
{
if (RAW_MODE_ONLY)
raw_mode_write = true;
else
raw_mode_write = mode;
}
bool is_raw_mode() const {
return is_raw_mode_read() && is_raw_mode_write();
}
bool is_raw_mode_read() const {
if (RAW_MODE_ONLY)
return true;
else
return raw_mode_read;
}
bool is_raw_mode_write() const {
if (RAW_MODE_ONLY)
return true;
else
return raw_mode_write;
}
void set_mutate(const TransportMutateStream::Ptr& mutate_arg)
{
mutate = mutate_arg;
}
bool send_queue_empty() const
{
return send_queue_size() == 0;
}
: Base(read_handler_arg, socket_arg, send_queue_max_size_arg,
free_list_max_size_arg, frame_context_arg, stats_arg)
{ }
// Called by LinkCommon and TCPTransport Client class
unsigned int send_queue_size() const
{
return queue.size()
return Base::queue.size()
#ifdef OPENVPN_GREMLIN
+ (gremlin ? gremlin->send_size() : 0)
#endif
;
}
bool send(BufferAllocated& b)
{
if (halt)
return false;
if (send_queue_max_size && queue.size() >= send_queue_max_size)
{
stats->error(Error::TCP_OVERFLOW);
read_handler->tcp_error_handler("TCP_OVERFLOW");
stop();
return false;
}
BufferPtr buf;
if (!free_list.empty())
{
buf = free_list.front();
free_list.pop_front();
}
else
buf.reset(new BufferAllocated());
buf->swap(b);
if (!is_raw_mode_write())
PacketStream::prepend_size(*buf);
if (mutate)
mutate->pre_send(*buf);
#ifdef OPENVPN_GREMLIN
if (gremlin)
gremlin_queue_send_buffer(buf);
else
#endif
queue_send_buffer(buf);
return true;
}
void inject(const Buffer& src)
{
const size_t size = src.size();
OPENVPN_LOG_TCPLINK_VERBOSE("TCP inject size=" << size);
if (size && !RAW_MODE_ONLY)
{
BufferAllocated buf;
frame_context.prepare(buf);
buf.write(src.c_data(), size);
BufferAllocated pkt;
put_pktstream(buf, pkt);
}
}
void start()
{
if (!halt)
queue_recv(nullptr);
}
void stop()
{
halt = true;
#ifdef OPENVPN_GREMLIN
if (gremlin)
gremlin->stop();
#endif
}
void reset_align_adjust(const size_t align_adjust)
{
frame_context.reset_align_adjust(align_adjust + (is_raw_mode() ? 0 : 2));
}
~Link() { stop(); }
private:
void queue_send_buffer(BufferPtr& buf)
// Called by LinkCommon
virtual void from_app_send_buffer(BufferPtr& buf) override
{
queue.push_back(std::move(buf));
if (queue.size() == 1) // send operation not currently active?
queue_send();
Base::queue_send_buffer(buf);
}
void queue_send()
{
BufferAllocated& buf = *queue.front();
socket.async_send(buf.const_buffer_clamp(),
[self=Ptr(this)](const openvpn_io::error_code& error, const size_t bytes_sent)
{
self->handle_send(error, bytes_sent);
});
}
void handle_send(const openvpn_io::error_code& error, const size_t bytes_sent)
{
if (!halt)
{
if (!error)
{
OPENVPN_LOG_TCPLINK_VERBOSE("TCP send raw=" << raw_mode_write << " size=" << bytes_sent);
stats->inc_stat(SessionStats::BYTES_OUT, bytes_sent);
stats->inc_stat(SessionStats::PACKETS_OUT, 1);
BufferPtr buf = queue.front();
if (bytes_sent == buf->size())
{
queue.pop_front();
if (free_list.size() < free_list_max_size)
{
buf->reset_content();
free_list.push_back(std::move(buf)); // recycle the buffer for later use
}
}
else if (bytes_sent < buf->size())
buf->advance(bytes_sent);
else
{
stats->error(Error::TCP_OVERFLOW);
read_handler->tcp_error_handler("TCP_INTERNAL_ERROR"); // error sent more bytes than we asked for
stop();
return;
}
}
else
{
OPENVPN_LOG_TCPLINK_ERROR("TCP send error: " << error.message());
stats->error(Error::NETWORK_SEND_ERROR);
read_handler->tcp_error_handler("NETWORK_SEND_ERROR");
stop();
return;
}
if (!queue.empty())
queue_send();
else
read_handler->tcp_write_queue_needs_send();
}
}
void queue_recv(PacketFrom *tcpfrom)
{
OPENVPN_LOG_TCPLINK_VERBOSE("TCPLink::queue_recv");
if (!tcpfrom)
tcpfrom = new PacketFrom();
frame_context.prepare(tcpfrom->buf);
socket.async_receive(frame_context.mutable_buffer_clamp(tcpfrom->buf),
[self=Ptr(this), tcpfrom=PacketFrom::SPtr(tcpfrom)](const openvpn_io::error_code& error, const size_t bytes_recvd) mutable
{
self->handle_recv(std::move(tcpfrom), error, bytes_recvd);
});
}
void handle_recv(PacketFrom::SPtr pfp, const openvpn_io::error_code& error, const size_t bytes_recvd)
{
OPENVPN_LOG_TCPLINK_VERBOSE("TCPLink::handle_recv: " << error.message());
if (!halt)
{
if (!error)
{
bool requeue = true;
OPENVPN_LOG_TCPLINK_VERBOSE("TCP recv raw=" << raw_mode_read << " size=" << bytes_recvd);
pfp->buf.set_size(bytes_recvd);
if (!is_raw_mode_read())
{
try {
BufferAllocated pkt;
requeue = put_pktstream(pfp->buf, pkt);
if (!pfp->buf.allocated() && pkt.allocated()) // recycle pkt allocated buffer
pfp->buf.move(pkt);
}
catch (const std::exception& e)
{
OPENVPN_LOG_TCPLINK_ERROR("TCP packet extract exception: " << e.what());
stats->error(Error::TCP_SIZE_ERROR);
read_handler->tcp_error_handler("TCP_SIZE_ERROR");
stop();
return;
}
}
else
{
if (mutate)
mutate->post_recv(pfp->buf);
#ifdef OPENVPN_GREMLIN
if (gremlin)
requeue = gremlin_recv(pfp->buf);
else
#endif
requeue = read_handler->tcp_read_handler(pfp->buf);
}
if (!halt && requeue)
queue_recv(pfp.release()); // reuse PacketFrom object
}
else if (error == openvpn_io::error::eof)
{
OPENVPN_LOG_TCPLINK_ERROR("TCP recv EOF");
read_handler->tcp_eof_handler();
}
else
{
OPENVPN_LOG_TCPLINK_ERROR("TCP recv error: " << error.message());
stats->error(Error::NETWORK_RECV_ERROR);
read_handler->tcp_error_handler("NETWORK_RECV_ERROR");
stop();
}
}
}
bool put_pktstream(BufferAllocated& buf, BufferAllocated& pkt)
virtual void recv_buffer(PacketFrom::SPtr& pfp, const size_t bytes_recvd) override
{
bool requeue = true;
stats->inc_stat(SessionStats::BYTES_IN, buf.size());
stats->inc_stat(SessionStats::PACKETS_IN, 1);
if (mutate)
mutate->post_recv(buf);
while (buf.size())
{
pktstream.put(buf, frame_context);
if (pktstream.ready())
{
pktstream.get(pkt);
#ifdef OPENVPN_GREMLIN
if (gremlin)
requeue = gremlin_recv(pkt);
else
#endif
requeue = read_handler->tcp_read_handler(pkt);
}
}
return requeue;
OPENVPN_LOG_TCPLINK_VERBOSE("TCP recv raw=" << Base::raw_mode_read << " size=" << bytes_recvd);
pfp->buf.set_size(bytes_recvd);
requeue = Base::process_recv_buffer(pfp->buf);
if (!Base::halt && requeue)
Base::queue_recv(pfp.release()); // reuse PacketFrom object
}
#ifdef OPENVPN_GREMLIN
void gremlin_queue_send_buffer(BufferPtr& buf)
{
gremlin->send_queue([self=Ptr(this), buf=std::move(buf)]() mutable {
if (!self->halt)
{
self->queue_send_buffer(buf);
}
});
}
bool gremlin_recv(BufferAllocated& buf)
{
gremlin->recv_queue([self=Ptr(this), buf=std::move(buf)]() mutable {
if (!self->halt)
{
const bool requeue = self->read_handler->tcp_read_handler(buf);
if (requeue)
self->queue_recv(nullptr);
}
});
return false;
}
#endif
typename Protocol::socket& socket;
bool halt;
bool raw_mode_read;
bool raw_mode_write;
ReadHandler read_handler;
Frame::Context frame_context;
SessionStats::Ptr stats;
const size_t send_queue_max_size;
const size_t free_list_max_size;
Queue queue; // send queue
Queue free_list; // recycled free buffers for send queue
PacketStream pktstream;
TransportMutateStream::Ptr mutate;
#ifdef OPENVPN_GREMLIN
std::unique_ptr<Gremlin::SendRecvQueue> gremlin;
#endif
};
}
} // namespace openvpn
+39
View File
@@ -0,0 +1,39 @@
// Copyright (C) 2012-2018 OpenVPN Inc.
// Base class for generic link objects.
#include <openvpn/buffer/buffer.hpp>
#include <openvpn/common/rc.hpp>
#pragma once
namespace openvpn
{
namespace TCPTransport
{
struct PacketFrom
{
typedef std::unique_ptr<PacketFrom> SPtr;
BufferAllocated buf;
};
class LinkBase : public RC<thread_unsafe_refcount>
{
protected:
virtual void recv_buffer(PacketFrom::SPtr& pfp,
const size_t bytes_recvd) = 0;
virtual void from_app_send_buffer(BufferPtr& buf) = 0;
public:
typedef RCPtr<LinkBase> Ptr;
virtual bool send_queue_empty() const = 0;
virtual unsigned int send_queue_size() const = 0;
virtual void reset_align_adjust(const size_t align_adjust) = 0;
virtual bool send(BufferAllocated& b) = 0;
virtual void set_raw_mode(const bool mode) = 0;
virtual void start() = 0;
virtual void stop() = 0;
};
}
}
+441
View File
@@ -0,0 +1,441 @@
// Copyright (C) 2012-2018 OpenVPN Inc.
// Base class for TCP link objects.
#ifndef OPENVPN_TRANSPORT_COMMONLINK_H
#define OPENVPN_TRANSPORT_COMMONLINK_H
#include <deque>
#include <utility> // for std::move
#include <memory>
#include <openvpn/io/io.hpp>
#include <openvpn/common/size.hpp>
#include <openvpn/common/rc.hpp>
#include <openvpn/common/socktypes.hpp>
#include <openvpn/frame/frame.hpp>
#include <openvpn/log/sessionstats.hpp>
#include <openvpn/transport/tcplinkbase.hpp>
#include <openvpn/transport/pktstream.hpp>
#include <openvpn/transport/mutate.hpp>
#ifdef OPENVPN_GREMLIN
#include <openvpn/transport/gremlin.hpp>
#endif
#if defined(OPENVPN_DEBUG_TCPLINK) && OPENVPN_DEBUG_TCPLINK >= 1
#define OPENVPN_LOG_TCPLINK_ERROR(x) OPENVPN_LOG(x)
#else
#define OPENVPN_LOG_TCPLINK_ERROR(x)
#endif
#if defined(OPENVPN_DEBUG_TCPLINK) && OPENVPN_DEBUG_TCPLINK >= 3
#define OPENVPN_LOG_TCPLINK_VERBOSE(x) OPENVPN_LOG(x)
#else
#define OPENVPN_LOG_TCPLINK_VERBOSE(x)
#endif
namespace openvpn {
namespace TCPTransport {
template <typename Protocol,
typename ReadHandler,
bool RAW_MODE_ONLY>
class LinkCommon : public LinkBase
{
typedef std::deque<BufferPtr> Queue;
public:
typedef RCPtr<LinkCommon<Protocol, ReadHandler, RAW_MODE_ONLY>> Ptr;
typedef Protocol protocol;
// In raw mode, data is sent and received without any special encapsulation.
// In non-raw mode, data is packetized by prepending a 16-bit length word
// onto each packet. The OpenVPN protocol runs in non-raw mode, while other
// TCP protocols such as HTTP or HTTPS would run in raw mode.
// This method is a no-op if RAW_MODE_ONLY is true.
void set_raw_mode(const bool mode)
{
set_raw_mode_read(mode);
set_raw_mode_write(mode);
}
void set_raw_mode_read(const bool mode)
{
if (RAW_MODE_ONLY)
raw_mode_read = true;
else
raw_mode_read = mode;
}
void set_raw_mode_write(const bool mode)
{
if (RAW_MODE_ONLY)
raw_mode_write = true;
else
raw_mode_write = mode;
}
void set_mutate(const TransportMutateStream::Ptr& mutate_arg)
{
mutate = mutate_arg;
}
bool send_queue_empty() const
{
return send_queue_size() == 0;
}
void inject(const Buffer& src)
{
const size_t size = src.size();
OPENVPN_LOG_TCPLINK_VERBOSE("TCP inject size=" << size);
if (size && !RAW_MODE_ONLY)
{
BufferAllocated buf;
frame_context.prepare(buf);
buf.write(src.c_data(), size);
BufferAllocated pkt;
put_pktstream(buf, pkt);
}
}
void start()
{
if (!halt)
queue_recv(nullptr);
}
void stop()
{
halt = true;
#ifdef OPENVPN_GREMLIN
if (gremlin)
gremlin->stop();
#endif
}
void reset_align_adjust(const size_t align_adjust)
{
frame_context.reset_align_adjust(align_adjust + (is_raw_mode() ? 0 : 2));
}
unsigned int send_queue_size() const
{
return queue.size()
#ifdef OPENVPN_GREMLIN
+ (gremlin ? gremlin->send_size() : 0)
#endif
;
}
bool send(BufferAllocated& b)
{
if (halt)
return false;
if (send_queue_max_size && send_queue_size() >= send_queue_max_size)
{
stats->error(Error::TCP_OVERFLOW);
read_handler->tcp_error_handler("TCP_OVERFLOW");
stop();
return false;
}
BufferPtr buf;
if (!free_list.empty())
{
buf = free_list.front();
free_list.pop_front();
}
else
buf.reset(new BufferAllocated());
buf->swap(b);
if (!is_raw_mode_write())
PacketStream::prepend_size(*buf);
if (mutate)
mutate->pre_send(*buf);
#ifdef OPENVPN_GREMLIN
if (gremlin)
gremlin_queue_send_buffer(buf);
else
#endif
from_app_send_buffer(buf);
return true;
}
void queue_recv(PacketFrom *tcpfrom)
{
OPENVPN_LOG_TCPLINK_VERBOSE("TLSLink::queue_recv");
if (!tcpfrom)
tcpfrom = new PacketFrom();
frame_context.prepare(tcpfrom->buf);
socket.async_receive(frame_context.mutable_buffer_clamp(tcpfrom->buf),
[self=Ptr(this), tcpfrom=PacketFrom::SPtr(tcpfrom)](const openvpn_io::error_code& error, const size_t bytes_recvd) mutable
{
try
{
self->handle_recv(std::move(tcpfrom), error, bytes_recvd);
}
catch (const std::exception& e)
{
OPENVPN_LOG_TCPLINK_ERROR("TCP packet extract exception: " << e.what());
self->stats->error(Error::TCP_SIZE_ERROR);
self->read_handler->tcp_error_handler("TCP_SIZE_ERROR");
self->stop();
}
});
}
protected:
LinkCommon(ReadHandler read_handler_arg,
typename Protocol::socket& socket_arg,
const size_t send_queue_max_size_arg, // 0 to disable
const size_t free_list_max_size_arg,
const Frame::Context& frame_context_arg,
const SessionStats::Ptr& stats_arg)
: socket(socket_arg),
halt(false),
read_handler(read_handler_arg),
frame_context(frame_context_arg),
stats(stats_arg),
send_queue_max_size(send_queue_max_size_arg),
free_list_max_size(free_list_max_size_arg)
{
set_raw_mode(false);
}
#ifdef OPENVPN_GREMLIN
void gremlin_config(const Gremlin::Config::Ptr& config)
{
if (config)
gremlin.reset(new Gremlin::SendRecvQueue(socket.get_executor().context(), config, true));
}
#endif
bool is_raw_mode() const {
return is_raw_mode_read() && is_raw_mode_write();
}
bool is_raw_mode_read() const {
if (RAW_MODE_ONLY)
return true;
else
return raw_mode_read;
}
bool is_raw_mode_write() const {
if (RAW_MODE_ONLY)
return true;
else
return raw_mode_write;
}
LinkCommon() { stop(); }
void queue_send_buffer(BufferPtr& buf)
{
queue.push_back(std::move(buf));
if (queue.size() == 1) // send operation not currently active?
queue_send();
}
void queue_send()
{
BufferAllocated& buf = *queue.front();
socket.async_send(buf.const_buffer_clamp(),
[self=Ptr(this)](const openvpn_io::error_code& error, const size_t bytes_sent)
{
self->handle_send(error, bytes_sent);
});
}
void handle_send(const openvpn_io::error_code& error, const size_t bytes_sent)
{
if (!halt)
{
if (!error)
{
OPENVPN_LOG_TCPLINK_VERBOSE("TLS-TCP send raw=" << raw_mode_write << " size=" << bytes_sent);
stats->inc_stat(SessionStats::BYTES_OUT, bytes_sent);
stats->inc_stat(SessionStats::PACKETS_OUT, 1);
BufferPtr buf = queue.front();
if (bytes_sent == buf->size())
{
queue.pop_front();
if (free_list.size() < free_list_max_size)
{
buf->reset_content();
free_list.push_back(std::move(buf)); // recycle the buffer for later use
}
}
else if (bytes_sent < buf->size())
buf->advance(bytes_sent);
else
{
stats->error(Error::TCP_OVERFLOW);
read_handler->tcp_error_handler("TCP_INTERNAL_ERROR"); // error sent more bytes than we asked for
stop();
return;
}
}
else
{
OPENVPN_LOG_TCPLINK_ERROR("TLS-TCP send error: " << error.message());
stats->error(Error::NETWORK_SEND_ERROR);
read_handler->tcp_error_handler("NETWORK_SEND_ERROR");
stop();
return;
}
if (!queue.empty())
queue_send();
else
tcp_write_queue_needs_send();
}
}
bool process_recv_buffer(BufferAllocated& buf)
{
bool requeue = true;
OPENVPN_LOG_TCPLINK_VERBOSE("TLSLink::process_recv_buffer: size=" << buf.size());
if (!is_raw_mode_read())
{
try {
BufferAllocated pkt;
requeue = put_pktstream(buf, pkt);
if (!buf.allocated() && pkt.allocated()) // recycle pkt allocated buffer
buf.move(pkt);
}
catch (const std::exception& e)
{
OPENVPN_LOG_TCPLINK_ERROR("TLS-TCP packet extract error: " << e.what());
stats->error(Error::TCP_SIZE_ERROR);
read_handler->tcp_error_handler("TCP_SIZE_ERROR");
stop();
return false;
}
}
else
{
if (mutate)
mutate->post_recv(buf);
#ifdef OPENVPN_GREMLIN
if (gremlin)
requeue = gremlin_recv(buf);
else
#endif
requeue = read_handler->tcp_read_handler(buf);
}
return requeue;
}
void handle_recv(PacketFrom::SPtr pfp, const openvpn_io::error_code& error, const size_t bytes_recvd)
{
OPENVPN_LOG_TCPLINK_VERBOSE("Link::handle_recv: " << error.message());
if (!halt)
{
if (!error)
{
recv_buffer(pfp, bytes_recvd);
}
else if (error == openvpn_io::error::eof)
{
OPENVPN_LOG_TCPLINK_ERROR("TCP recv EOF");
read_handler->tcp_eof_handler();
}
else
{
OPENVPN_LOG_TCPLINK_ERROR("TCP recv error: " << error.message());
stats->error(Error::NETWORK_RECV_ERROR);
read_handler->tcp_error_handler("NETWORK_RECV_ERROR");
stop();
}
}
}
bool put_pktstream(BufferAllocated& buf, BufferAllocated& pkt)
{
bool requeue = true;
stats->inc_stat(SessionStats::BYTES_IN, buf.size());
stats->inc_stat(SessionStats::PACKETS_IN, 1);
if (mutate)
mutate->post_recv(buf);
while (buf.size())
{
pktstream.put(buf, frame_context);
if (pktstream.ready())
{
pktstream.get(pkt);
#ifdef OPENVPN_GREMLIN
if (gremlin)
requeue = gremlin_recv(pkt);
else
#endif
requeue = read_handler->tcp_read_handler(pkt);
}
}
return requeue;
}
#ifdef OPENVPN_GREMLIN
void gremlin_queue_send_buffer(BufferPtr& buf)
{
gremlin->send_queue([self=Ptr(this), buf=std::move(buf)]() mutable {
if (!self->halt)
{
self->queue_send_buffer(buf);
}
});
}
bool gremlin_recv(BufferAllocated& buf)
{
gremlin->recv_queue([self=Ptr(this), buf=std::move(buf)]() mutable {
if (!self->halt)
{
const bool requeue = self->read_handler->tcp_read_handler(buf);
if (requeue)
self->queue_recv(nullptr);
}
});
return false;
}
#endif
void tcp_write_queue_needs_send()
{
read_handler->tcp_write_queue_needs_send();
}
typename Protocol::socket& socket;
bool halt;
ReadHandler read_handler;
Frame::Context frame_context;
SessionStats::Ptr stats;
const size_t send_queue_max_size;
const size_t free_list_max_size;
Queue queue; // send queue
Queue free_list; // recycled free buffers for send queue
PacketStream pktstream;
TransportMutateStream::Ptr mutate;
bool raw_mode_read;
bool raw_mode_write;
#ifdef OPENVPN_GREMLIN
std::unique_ptr<Gremlin::SendRecvQueue> gremlin;
#endif
private:
virtual void recv_buffer(PacketFrom::SPtr& pfp, const size_t bytes_recvd) = 0;
virtual void from_app_send_buffer(BufferPtr& buf) = 0;
};
}
} // namespace openvpn
#endif