Merge commit '86cc97e55fe346502462284d2e636a2b3708163e' as 'Sources/OpenVPN3'

This commit is contained in:
Sergey Abramchuk
2020-02-24 14:43:11 +03:00
655 changed files with 146468 additions and 0 deletions
@@ -0,0 +1,345 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_SERVER_LISTENLIST_H
#define OPENVPN_SERVER_LISTENLIST_H
#include <string>
#include <vector>
#include <utility> // for std::move
#include <openvpn/common/platform.hpp>
#include <openvpn/common/exception.hpp>
#include <openvpn/common/options.hpp>
#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>
namespace openvpn {
namespace Listen {
struct Item
{
enum SSLMode {
SSLUnspecified,
SSLOn,
SSLOff,
#ifdef OPENVPN_POLYSOCK_SUPPORTS_ALT_ROUTING
AltRouting,
#endif
};
std::string directive;
std::string addr;
std::string port;
Protocol proto;
SSLMode ssl = SSLUnspecified;
unsigned int n_threads = 0;
std::string to_string() const
{
std::string ret;
ret += directive + ' ' + addr;
if (!proto.is_local())
ret += ' ' + port;
ret += ' ' + std::string(proto.str()) + ' ' + openvpn::to_string(n_threads);
switch (ssl)
{
case SSLUnspecified:
break;
case SSLOn:
ret += " ssl";
break;
case SSLOff:
ret += " !ssl";
break;
#ifdef OPENVPN_POLYSOCK_SUPPORTS_ALT_ROUTING
case AltRouting:
ret += " alt";
break;
#endif
}
return ret;
}
Item port_offset(const unsigned int offset) const
{
Item ret(*this);
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;
}
};
class List : public std::vector<Item>
{
public:
enum LoadMode {
Nominal,
AllowDefault,
AllowEmpty
};
List() {}
List(const Item& item)
{
push_back(item);
}
List(Item&& item)
{
push_back(std::move(item));
}
List(const OptionList& opt,
const std::string& directive,
const LoadMode load_mode,
const unsigned int n_cores)
{
size_t n_listen = 0;
for (auto &o : opt)
{
if (match(directive, o))
++n_listen;
}
if (n_listen)
{
reserve(n_listen);
for (auto &o : opt)
{
if (match(directive, o))
{
o.touch();
unsigned int mult = 1;
int local = 0;
Item e;
// directive
e.directive = o.get(0, 64);
// IP address
e.addr = o.get(1, 128);
// port number
e.port = o.get(2, 16);
if (Protocol::is_local_type(e.port))
{
local = 1;
e.port = "";
}
else
HostPort::validate_port(e.port, e.directive);
// protocol
{
const std::string title = e.directive + " protocol";
e.proto = Protocol::parse(o.get(3-local, 16), Protocol::NO_SUFFIX, title.c_str());
}
if (!local)
{
// modify protocol based on IP version of given address
const std::string title = e.directive + " addr";
const IP::Addr addr = IP::Addr(e.addr, title.c_str());
e.proto.mod_addr_version(addr);
}
// number of threads
int n_threads_exists = 0;
{
const std::string ntstr = o.get_optional(4-local, 16);
if (ntstr.length() > 0 && string::is_digit(ntstr[0]))
n_threads_exists = 1;
}
if (n_threads_exists)
{
std::string n_threads = o.get(4-local, 16);
if (string::ends_with(n_threads, "*N"))
{
mult = n_cores;
n_threads = n_threads.substr(0, n_threads.length() - 2);
}
if (!parse_number_validate<unsigned int>(n_threads, 3, 1, 100, &e.n_threads))
OPENVPN_THROW(option_error, e.directive << ": bad num threads: " << n_threads);
#ifndef OPENVPN_PLATFORM_WIN
if (local && e.n_threads != 1)
OPENVPN_THROW(option_error, e.directive << ": local socket only supports one thread per pathname (not " << n_threads << ')');
#endif
e.n_threads *= mult;
}
else
e.n_threads = 1;
// SSL
if (o.size() >= 5-local+n_threads_exists)
{
const std::string& ssl_qualifier = o.get(4-local+n_threads_exists, 16);
if (ssl_qualifier == "ssl")
{
if (local)
OPENVPN_THROW(option_error, e.directive << ": SSL not supported on local sockets");
e.ssl = Item::SSLOn;
}
else if (ssl_qualifier == "!ssl")
e.ssl = Item::SSLOff;
#ifdef OPENVPN_POLYSOCK_SUPPORTS_ALT_ROUTING
else if (ssl_qualifier == "alt")
e.ssl = Item::AltRouting;
#endif
else
OPENVPN_THROW(option_error, e.directive << ": unrecognized SSL qualifier");
}
push_back(std::move(e));
}
}
}
else if (load_mode == AllowDefault)
{
Item e;
// parse "proto" option if present
{
const Option* o = opt.get_ptr("proto");
if (o)
e.proto = Protocol::parse(o->get(1, 16), Protocol::SERVER_SUFFIX);
else
e.proto = Protocol(Protocol::UDPv4);
}
// parse "port" option if present
{
const Option* o = opt.get_ptr("lport");
if (!o)
o = opt.get_ptr("port");
if (o)
{
e.port = o->get(1, 16);
HostPort::validate_port(e.port, "listen");
}
else
e.port = "1194";
}
// parse "local" option if present
{
const Option* o = opt.get_ptr("local");
if (o)
{
e.addr = o->get(1, 128);
const IP::Addr addr = IP::Addr(e.addr, "local addr");
e.proto.mod_addr_version(addr);
}
else if (e.proto.is_ipv6())
e.addr = "::0";
else
e.addr = "0.0.0.0";
}
// n_threads defaults to one unless "listen" directive is used
e.n_threads = 1;
push_back(std::move(e));
}
else if (load_mode != AllowEmpty)
OPENVPN_THROW(option_error, "no " << directive << " directives found");
}
unsigned int total_threads() const
{
unsigned int ret = 0;
for (auto &i : *this)
ret += i.n_threads;
return ret;
}
std::string to_string() const
{
std::string ret;
for (auto &i : *this)
{
ret += i.to_string();
ret += '\n';
}
return ret;
}
std::string local_addr() const
{
for (auto &i : *this)
if (i.proto.is_local())
return i.addr;
return std::string();
}
List expand_ports_by_n_threads(const size_t max_size) const
{
List ret;
for (const auto &e : *this)
{
unsigned int offset = 0;
do {
if (ret.size() >= max_size)
OPENVPN_THROW(option_error, e.directive << ": max_size=" << max_size << " exceeded");
ret.emplace_back(e.port_offset(offset));
} while (++offset < e.n_threads);
}
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)
{
const size_t len = directive.length();
if (len && o.size())
{
if (directive[len-1] == '-')
return string::starts_with(o.ref(0), directive);
else
return o.ref(0) == directive;
}
else
return false;
}
};
}
}
#endif
+150
View File
@@ -0,0 +1,150 @@
// 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/>.
// Server-side client manager
#ifndef OPENVPN_SERVER_MANAGE_H
#define OPENVPN_SERVER_MANAGE_H
#include <string>
#include <vector>
#include <openvpn/common/size.hpp>
#include <openvpn/common/exception.hpp>
#include <openvpn/common/rc.hpp>
#include <openvpn/tun/server/tunbase.hpp>
#include <openvpn/addr/route.hpp>
#include <openvpn/auth/authcreds.hpp>
#include <openvpn/ssl/proto.hpp>
#include <openvpn/server/servhalt.hpp>
#include <openvpn/server/peerstats.hpp>
#include <openvpn/server/peeraddr.hpp>
#include <openvpn/auth/authcert.hpp>
#include <openvpn/auth/authstatusconst.hpp>
namespace openvpn {
namespace ManClientInstance {
// Base class for the per-client-instance state of the ManServer.
// Each client instance uses this class to send data to the man layer.
struct Send : public virtual RC<thread_unsafe_refcount>
{
typedef RCPtr<Send> Ptr;
virtual void pre_stop() = 0;
virtual void stop() = 0;
virtual void auth_request(const AuthCreds::Ptr& auth_creds,
const AuthCert::Ptr& auth_cert,
const PeerAddr::Ptr& peer_addr) = 0;
virtual void push_request(ProtoContext::Config::Ptr pconf) = 0;
// INFO notification
virtual void info_request(const std::string& imsg) = 0;
// bandwidth stats notification
virtual void stats_notify(const PeerStats& ps, const bool final) = 0;
// client float notification
virtual void float_notify(const PeerAddr::Ptr& addr) = 0;
// ID
virtual std::string instance_name() const = 0;
virtual std::uint64_t instance_id() const = 0;
// return a JSON string describing connected user
virtual std::string describe_user(const bool show_userprop) = 0;
// disconnect
virtual void disconnect_user(const HaltRestart::Type type,
const AuthStatus::Type auth_status,
const std::string& reason,
const bool tell_client) = 0;
// send control channel message
virtual void post_info_user(BufferPtr&& info) = 0;
// set ACL index for user
virtual void set_acl_index(const int acl_index,
const std::string* username,
const bool challenge) = 0;
// notify of local user properties update
virtual void userprop_local_update() = 0;
};
// Base class for the client instance receiver. Note that all
// client instance receivers (transport, routing, management,
// etc.) must inherit virtually from RC because the client instance
// object will inherit from multiple receivers.
struct Recv : public virtual RC<thread_unsafe_refcount>
{
typedef RCPtr<Recv> Ptr;
virtual void stop() = 0;
virtual void auth_failed(const std::string& reason,
const bool tell_client) = 0;
virtual void push_reply(std::vector<BufferPtr>&& push_msgs) = 0;
// push a halt or restart message to client
virtual void push_halt_restart_msg(const HaltRestart::Type type,
const std::string& reason,
const bool tell_client) = 0;
// send control channel message
virtual void post_cc_msg(BufferPtr&& msg) = 0;
// schedule a low-level connection disconnect in seconds
virtual void schedule_disconnect(const unsigned int seconds) = 0;
// schedule an auth pending disconnect in seconds
virtual void schedule_auth_pending_timeout(const unsigned int seconds) = 0;
// set up relay to target
virtual void relay(const IP::Addr& target, const int port) = 0;
// 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;
};
struct Factory : public RC<thread_unsafe_refcount>
{
typedef RCPtr<Factory> Ptr;
virtual void start() = 0;
virtual void stop() = 0;
virtual Send::Ptr new_obj(Recv* instance) = 0;
};
}
}
#endif
@@ -0,0 +1,70 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_SERVER_PEERADDR_H
#define OPENVPN_SERVER_PEERADDR_H
#include <cstdint> // for std::uint32_t, uint64_t, etc.
#include <openvpn/common/rc.hpp>
#include <openvpn/common/to_string.hpp>
#include <openvpn/addr/ip.hpp>
namespace openvpn {
struct AddrPort
{
AddrPort() : port(0) {}
std::string to_string() const
{
return addr.to_string_bracket_ipv6() + ':' + openvpn::to_string(port);
}
IP::Addr addr;
std::uint16_t port;
};
struct PeerAddr : public RC<thread_unsafe_refcount>
{
typedef RCPtr<PeerAddr> Ptr;
PeerAddr()
: tcp(false)
{
}
std::string to_string() const
{
std::string proto;
if (tcp)
proto = "TCP ";
else
proto = "UDP ";
return proto + remote.to_string() + " -> " + local.to_string();
}
AddrPort remote;
AddrPort local;
bool tcp;
};
}
#endif
@@ -0,0 +1,45 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_SERVER_PEERSTATS_H
#define OPENVPN_SERVER_PEERSTATS_H
#include <cstdint> // for std::uint32_t, uint64_t, etc.
namespace openvpn {
struct PeerStats
{
PeerStats()
: rx_bytes(0),
tx_bytes(0),
status(0)
{
}
std::uint64_t rx_bytes;
std::uint64_t tx_bytes;
int status;
};
}
#endif
@@ -0,0 +1,38 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_SERVER_SERVHALT_H
#define OPENVPN_SERVER_SERVHALT_H
namespace openvpn {
namespace HaltRestart {
enum Type {
HALT, // disconnect
RESTART, // restart, don't preserve session token
RESTART_PSID, // restart, preserve session token
RESTART_PASSIVE, // restart, preserve session token and local client instance object
AUTH_FAILED, // auth fail, don't preserve session token
RAW, // pass raw message to client
};
}
}
#endif
@@ -0,0 +1,752 @@
// 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/>.
// OpenVPN protocol implementation for client-instance object on server
#ifndef OPENVPN_SERVER_SERVPROTO_H
#define OPENVPN_SERVER_SERVPROTO_H
#include <memory>
#include <utility> // for std::move
#include <openvpn/common/size.hpp>
#include <openvpn/common/exception.hpp>
#include <openvpn/common/rc.hpp>
#include <openvpn/common/unicode.hpp>
#include <openvpn/common/abort.hpp>
#include <openvpn/common/link.hpp>
#include <openvpn/common/string.hpp>
#include <openvpn/buffer/bufstream.hpp>
#include <openvpn/time/asiotimer.hpp>
#include <openvpn/time/coarsetime.hpp>
#include <openvpn/crypto/cryptodc.hpp>
#include <openvpn/ssl/proto.hpp>
#include <openvpn/transport/server/transbase.hpp>
#include <openvpn/tun/server/tunbase.hpp>
#include <openvpn/server/manage.hpp>
#ifdef OPENVPN_DEBUG_SERVPROTO
#define OPENVPN_LOG_SERVPROTO(x) OPENVPN_LOG(x)
#else
#define OPENVPN_LOG_SERVPROTO(x)
#endif
namespace openvpn {
class ServerProto
{
typedef ProtoContext Base;
typedef Link<TransportClientInstance::Send, TransportClientInstance::Recv> TransportLink;
typedef Link<TunClientInstance::Send, TunClientInstance::Recv> TunLink;
typedef Link<ManClientInstance::Send, ManClientInstance::Recv> ManLink;
public:
class Session;
class Factory : public TransportClientInstance::Factory
{
public:
typedef RCPtr<Factory> Ptr;
typedef Base::Config ProtoConfig;
Factory(openvpn_io::io_context& io_context_arg,
const Base::Config& c)
: io_context(io_context_arg)
{
if (c.tls_crypt_enabled())
preval.reset(new Base::TLSCryptPreValidate(c, true));
else if (c.tls_auth_enabled())
preval.reset(new Base::TLSAuthPreValidate(c, true));
}
virtual TransportClientInstance::Recv::Ptr new_client_instance() override;
virtual bool validate_initial_packet(const BufferAllocated& net_buf) override
{
if (preval)
{
const bool ret = preval->validate(net_buf);
if (!ret)
stats->error(Error::TLS_AUTH_FAIL);
return ret;
}
else
return true;
}
ProtoConfig::Ptr clone_proto_config() const
{
return new ProtoConfig(*proto_context_config);
}
openvpn_io::io_context& io_context;
ProtoConfig::Ptr proto_context_config;
ManClientInstance::Factory::Ptr man_factory;
TunClientInstance::Factory::Ptr tun_factory;
SessionStats::Ptr stats;
private:
Base::TLSWrapPreValidate::Ptr preval;
};
// This is the main server-side client instance object
class Session : Base, // OpenVPN protocol implementation
public TransportLink, // Transport layer
public TunLink, // Tun/routing layer
public ManLink // Management layer
{
friend class Factory; // calls constructor
typedef Base::PacketType PacketType;
using Base::now;
using Base::stat;
public:
typedef RCPtr<Session> Ptr;
virtual bool defined() const override
{
return defined_();
}
virtual TunClientInstance::Recv* override_tun(TunClientInstance::Send* tun) override
{
TunLink::send.reset(tun);
return this;
}
virtual void start(const TransportClientInstance::Send::Ptr& parent,
const PeerAddr::Ptr& addr,
const int local_peer_id) override
{
TransportLink::send = parent;
peer_addr = addr;
// init OpenVPN protocol handshake
Base::update_now();
Base::reset();
Base::set_local_peer_id(local_peer_id);
Base::start();
Base::flush(true);
// coarse wakeup range
housekeeping_schedule.init(Time::Duration::binary_ms(512), Time::Duration::binary_ms(1024));
}
virtual PeerStats stats_poll() override
{
if (TransportLink::send)
return TransportLink::send->stats_poll();
else
return PeerStats();
}
virtual bool should_preserve_session_id() override
{
return preserve_session_id;
}
virtual void stop() override
{
if (!halt)
{
halt = true;
housekeeping_timer.cancel();
if (ManLink::send)
ManLink::send->pre_stop();
// deliver final peer stats to management layer
if (TransportLink::send && ManLink::send)
{
if (TransportLink::send->stats_pending())
ManLink::send->stats_notify(TransportLink::send->stats_poll(), true);
}
Base::pre_destroy();
Base::reset_dc_factory();
if (TransportLink::send)
{
TransportLink::send->stop();
TransportLink::send.reset();
}
if (TunLink::send)
{
TunLink::send->stop();
TunLink::send.reset();
}
if (ManLink::send)
{
ManLink::send->stop();
ManLink::send.reset();
}
}
}
// called with OpenVPN-encapsulated packets from transport layer
virtual bool transport_recv(BufferAllocated& buf) override
{
bool ret = false;
if (!Base::primary_defined())
return false;
try {
OPENVPN_LOG_SERVPROTO("Transport RECV[" << buf.size() << "] " << client_endpoint_render() << ' ' << Base::dump_packet(buf));
// update current time
Base::update_now();
// get packet type
Base::PacketType pt = Base::packet_type(buf);
// process packet
if (pt.is_data())
{
// data packet
ret = Base::data_decrypt(pt, buf);
if (buf.size())
{
#ifdef OPENVPN_PACKET_LOG
log_packet(buf, false);
#endif
// make packet appear as incoming on tun interface
if (true) // fixme: was tun
{
OPENVPN_LOG_SERVPROTO("TUN SEND[" << buf.size() << ']');
// fixme -- code me
}
}
// do a lightweight flush
Base::flush(false);
}
else if (pt.is_control())
{
// control packet
ret = Base::control_net_recv(pt, std::move(buf));
// do a full flush
Base::flush(true);
}
// schedule housekeeping wakeup
set_housekeeping_timer();
}
catch (const std::exception& e)
{
error(e);
ret = false;
}
return ret;
}
// called with cleartext IP packets from routing layer
virtual void tun_recv(BufferAllocated& buf) override
{
// fixme -- code me
}
// Return true if keepalive parameter(s) are enabled.
virtual bool is_keepalive_enabled() const override
{
return Base::is_keepalive_enabled();
}
// Disable keepalive for rest of session, but fetch
// the keepalive parameters (in seconds).
virtual void disable_keepalive(unsigned int& keepalive_ping,
unsigned int& keepalive_timeout) override
{
Base::disable_keepalive(keepalive_ping, keepalive_timeout);
}
// override the data channel factory
virtual void override_dc_factory(const CryptoDCFactory::Ptr& dc_factory) override
{
Base::dc_settings().set_factory(dc_factory);
}
virtual ~Session()
{
// fatal error if destructor called while Session is active
if (defined_())
std::abort();
}
private:
Session(openvpn_io::io_context& io_context_arg,
const Factory& factory,
ManClientInstance::Factory::Ptr man_factory_arg,
TunClientInstance::Factory::Ptr tun_factory_arg)
: Base(factory.clone_proto_config(), factory.stats),
io_context(io_context_arg),
housekeeping_timer(io_context_arg),
disconnect_at(Time::infinite()),
stats(factory.stats),
man_factory(man_factory_arg),
tun_factory(tun_factory_arg)
{}
bool defined_() const
{
return !halt && TransportLink::send;
}
// proto base class calls here for control channel network sends
virtual void control_net_send(const Buffer& net_buf) override
{
OPENVPN_LOG_SERVPROTO("Transport SEND[" << net_buf.size() << "] " << client_endpoint_render() << ' ' << Base::dump_packet(net_buf));
if (TransportLink::send)
{
if (TransportLink::send->transport_send_const(net_buf))
Base::update_last_sent();
}
}
// Called on server with credentials and peer info provided by client.
// Should be overriden by derived class if credentials are required.
virtual void server_auth(const std::string& username,
const SafeString& password,
const std::string& peer_info,
const AuthCert::Ptr& auth_cert) override
{
constexpr size_t MAX_USERNAME_SIZE = 256;
constexpr size_t MAX_PASSWORD_SIZE = 16384;
if (get_management())
{
AuthCreds::Ptr auth_creds(new AuthCreds(Unicode::utf8_printable(username, MAX_USERNAME_SIZE|Unicode::UTF8_FILTER),
Unicode::utf8_printable(password, MAX_PASSWORD_SIZE|Unicode::UTF8_FILTER|Unicode::UTF8_PASS_FMT),
Unicode::utf8_printable(peer_info, Unicode::UTF8_FILTER|Unicode::UTF8_PASS_FMT)));
ManLink::send->auth_request(auth_creds, auth_cert, peer_addr);
}
}
// proto base class calls here for app-level control-channel messages received
virtual void control_recv(BufferPtr&& app_bp) override
{
const std::string msg = Unicode::utf8_printable(Base::template read_control_string<std::string>(*app_bp),
Unicode::UTF8_FILTER);
if (msg == "PUSH_REQUEST")
{
if (get_management())
ManLink::send->push_request(Base::conf_ptr());
else
auth_failed("no management provider", false);
}
else if (string::starts_with(msg, "INFO,"))
{
if (get_management())
ManLink::send->info_request(msg.substr(5));
}
else
{
OPENVPN_LOG("Unrecognized client request: " << msg);
}
}
virtual void auth_failed(const std::string& reason,
const bool tell_client) override
{
push_halt_restart_msg(HaltRestart::AUTH_FAILED, reason, tell_client);
}
virtual void relay(const IP::Addr& target, const int port) override
{
if (halt || disconnect_type == DT_HALT_RESTART)
return;
Base::update_now();
if (TunLink::send && (disconnect_type < DT_RELAY_TRANSITION))
{
disconnect_type = DT_RELAY_TRANSITION;
TunLink::send->relay(target, port);
disconnect_in(Time::Duration::seconds(10)); // not a real disconnect, just complete transition to relay
}
if (Base::primary_defined())
{
BufferPtr buf(new BufferAllocated(64, 0));
buf_append_string(*buf, "RELAY");
buf->null_terminate();
Base::control_send(std::move(buf));
Base::flush(true);
}
set_housekeeping_timer();
}
virtual void push_reply(std::vector<BufferPtr>&& push_msgs) override
{
if (halt || (disconnect_type >= DT_RELAY_TRANSITION) || !Base::primary_defined())
return;
if (disconnect_type == DT_AUTH_PENDING)
{
disconnect_type = DT_NONE;
cancel_disconnect();
}
Base::update_now();
if (get_tun())
{
Base::init_data_channel();
for (auto &msg : push_msgs)
{
msg->null_terminate();
Base::control_send(std::move(msg));
}
Base::flush(true);
set_housekeeping_timer();
}
else
{
auth_failed("no tun provider", false);
}
}
virtual TunClientInstance::NativeHandle tun_native_handle() override
{
if (get_tun())
return TunLink::send->tun_native_handle();
else
return TunClientInstance::NativeHandle();
}
virtual void push_halt_restart_msg(const HaltRestart::Type type,
const std::string& reason,
const bool tell_client) override
{
if (halt || disconnect_type == DT_HALT_RESTART)
return;
Base::update_now();
BufferPtr buf(new BufferAllocated(128, BufferAllocated::GROW));
BufferStreamOut os(*buf);
std::string ts;
switch (type)
{
case HaltRestart::HALT:
ts = "HALT";
os << "HALT,";
if (tell_client && !reason.empty())
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";
os << "RESTART,";
if (tell_client && !reason.empty())
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";
os << "RESTART,[P]:";
if (tell_client && !reason.empty())
os << reason;
else
os << "server requested a client reconnect";
break;
case HaltRestart::RESTART_PSID:
ts = "RESTART_PSID";
os << "RESTART,[P]:";
if (tell_client && !reason.empty())
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:
{
const size_t pos = reason.find_first_of(',');
if (pos != std::string::npos)
ts = reason.substr(0, pos);
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 (Base::primary_defined())
{
buf->null_terminate();
Base::control_send(std::move(buf));
Base::flush(true);
}
set_housekeeping_timer();
}
virtual void schedule_disconnect(const unsigned int seconds)
{
if (halt || disconnect_type == DT_HALT_RESTART)
return;
Base::update_now();
disconnect_in(Time::Duration::seconds(seconds));
set_housekeeping_timer();
}
virtual void schedule_auth_pending_timeout(const unsigned int seconds)
{
if (halt || (disconnect_type >= DT_RELAY_TRANSITION) || !seconds)
return;
Base::update_now();
disconnect_type = DT_AUTH_PENDING;
disconnect_in(Time::Duration::seconds(seconds));
set_housekeeping_timer();
}
virtual void post_cc_msg(BufferPtr&& msg) override
{
if (halt || !Base::primary_defined())
return;
Base::update_now();
msg->null_terminate();
Base::control_send(std::move(msg));
Base::flush(true);
set_housekeeping_timer();
}
virtual void stats_notify(const PeerStats& ps, const bool final) override
{
if (ManLink::send)
ManLink::send->stats_notify(ps, final);
}
virtual void float_notify(const PeerAddr::Ptr& addr) override
{
if (ManLink::send)
ManLink::send->float_notify(addr);
}
virtual void data_limit_notify(const int key_id,
const DataLimit::Mode cdl_mode,
const DataLimit::State cdl_status) override
{
Base::update_now();
Base::data_limit_notify(key_id, cdl_mode, cdl_status);
Base::flush(true);
set_housekeeping_timer();
}
bool get_management()
{
if (!ManLink::send)
{
if (man_factory)
ManLink::send = man_factory->new_obj(this);
}
return bool(ManLink::send);
}
bool get_tun()
{
if (!TunLink::send)
{
if (tun_factory)
TunLink::send = tun_factory->new_obj(this);
}
return bool(TunLink::send);
}
// caller must ensure that update_now() was called before
// and set_housekeeping_timer() called after this method
void disconnect_in(const Time::Duration& dur)
{
disconnect_at = now() + dur;
}
void cancel_disconnect()
{
disconnect_at = Time::infinite();
}
void housekeeping_callback(const openvpn_io::error_code& e)
{
try {
if (!e && !halt)
{
// update current time
Base::update_now();
housekeeping_schedule.reset();
Base::housekeeping();
if (Base::invalidated())
invalidation_error(Base::invalidation_reason());
else if (now() >= disconnect_at)
{
switch (disconnect_type)
{
case DT_HALT_RESTART:
error("disconnect triggered");
break;
case DT_RELAY_TRANSITION:
Base::pre_destroy();
break;
case DT_AUTH_PENDING:
auth_failed("Auth Pending Timeout", true);
break;
default:
error("unknown disconnect");
break;
}
}
else
set_housekeeping_timer();
}
}
catch (const std::exception& e)
{
error(e);
}
}
void set_housekeeping_timer()
{
Time next = Base::next_housekeeping();
next.min(disconnect_at);
if (!housekeeping_schedule.similar(next))
{
if (!next.is_infinite())
{
next.max(now());
housekeeping_schedule.reset(next);
housekeeping_timer.expires_at(next);
housekeeping_timer.async_wait([self=Ptr(this)](const openvpn_io::error_code& error)
{
self->housekeeping_callback(error);
});
}
else
{
housekeeping_timer.cancel();
housekeeping_schedule.reset();
}
}
}
std::string client_endpoint_render()
{
if (TransportLink::send)
return TransportLink::send->transport_info();
else
return "";
}
void error(const std::string& error)
{
OPENVPN_LOG("ServerProto: " << error);
stop();
}
void error(const std::exception& e)
{
error(e.what());
}
void error()
{
stop();
}
void invalidation_error(const Error::Type err)
{
switch (err)
{
case Error::KEV_NEGOTIATE_ERROR:
case Error::KEEPALIVE_TIMEOUT:
error();
break;
default:
error(std::string("Session invalidated: ") + Error::name(err));
break;
}
}
openvpn_io::io_context& io_context;
// higher values are higher priority
enum DisconnectType {
DT_NONE=0,
DT_AUTH_PENDING,
DT_RELAY_TRANSITION,
DT_HALT_RESTART,
};
int disconnect_type = DT_NONE;
bool preserve_session_id = true;
bool halt = false;
PeerAddr::Ptr peer_addr;
CoarseTime housekeeping_schedule;
AsioTimer housekeeping_timer;
Time disconnect_at;
SessionStats::Ptr stats;
ManClientInstance::Factory::Ptr man_factory;
TunClientInstance::Factory::Ptr tun_factory;
};
};
inline TransportClientInstance::Recv::Ptr ServerProto::Factory::new_client_instance()
{
return new Session(io_context, *this, man_factory, tun_factory);
}
}
#endif
@@ -0,0 +1,223 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_SERVER_VPNSERVNETBLOCK_H
#define OPENVPN_SERVER_VPNSERVNETBLOCK_H
#include <sstream>
#include <openvpn/common/size.hpp>
#include <openvpn/common/exception.hpp>
#include <openvpn/common/options.hpp>
#include <openvpn/addr/route.hpp>
#include <openvpn/addr/range.hpp>
namespace openvpn {
class VPNServerNetblock
{
public:
OPENVPN_EXCEPTION(vpn_serv_netblock);
struct Netblock
{
Netblock() {}
Netblock(const IP::Route& route)
{
if (!route.is_canonical())
throw vpn_serv_netblock("not canonical");
if (route.host_bits() < 2)
throw vpn_serv_netblock("need at least 4 addresses in netblock");
net = route.addr;
server_gw = net + 1;
prefix_len = route.prefix_len;
}
bool defined() const { return net.defined(); }
IP::Addr netmask() const
{
return IP::Addr::netmask_from_prefix_len(net.version(), prefix_len);
}
bool contains(const IP::Addr& a) const
{
if (net.defined() && net.version() == a.version())
return (a & netmask()) == net;
else
return false;
}
std::string to_string() const
{
return '[' + net.to_string() + '/' + std::to_string(prefix_len) + ',' + server_gw.to_string() + ']';
}
IP::Addr net;
IP::Addr server_gw;
unsigned int prefix_len = 0;
};
struct ClientNetblock : public Netblock
{
ClientNetblock() {}
ClientNetblock(const IP::Route& route)
: Netblock(route)
{
const size_t extent = route.extent();
bcast = net + (extent - 1);
clients = IP::Range(net + 2, extent - 3);
}
std::string to_string() const
{
return '[' + Netblock::to_string() + ','
+ clients.to_string() + ','
+ bcast.to_string() + ']';
}
IP::Range clients;
IP::Addr bcast;
};
class PerThread
{
friend class VPNServerNetblock;
public:
const IP::Range& range4() const { return range4_; }
bool range6_defined() const { return range6_.defined(); }
const IP::Range& range6() const { return range6_; }
private:
IP::Range range4_;
IP::Range range6_;
};
VPNServerNetblock(const OptionList& opt,
const std::string& opt_name,
const bool ipv4_optional,
const unsigned int n_threads)
{
// ifconfig
if (!ipv4_optional || opt.exists(opt_name))
{
const Option& o = opt.get(opt_name);
const IP::Addr gw(o.get(1, 64), opt_name + " gateway");
const IP::Addr nm(o.get(2, 64), opt_name + " netmask");
IP::Route rt(gw, nm.prefix_len());
if (rt.version() != IP::Addr::V4)
throw vpn_serv_netblock(opt_name + " address is not IPv4");
rt.force_canonical();
snb4 = ClientNetblock(rt);
if (snb4.server_gw != gw)
throw vpn_serv_netblock(opt_name + " local gateway must be first usable address of subnet");
}
// ifconfig-ipv6
{
const Option* o = opt.get_ptr(opt_name + "-ipv6");
if (o)
{
IP::Route rt(o->get(1, 64), opt_name + "-ipv6 network");
if (rt.version() != IP::Addr::V6)
throw vpn_serv_netblock(opt_name + "-ipv6 network is not IPv6");
if (!rt.is_canonical())
throw vpn_serv_netblock(opt_name + "-ipv6 network is not canonical");
snb6 = ClientNetblock(rt);
}
}
if (n_threads)
{
// IPv4 per-thread partition
{
IP::RangePartition rp(snb4.clients, n_threads);
IP::Range crange;
for (unsigned int i = 0; i < n_threads; ++i)
{
if (!rp.next(crange))
throw vpn_serv_netblock(opt_name + " : unexpected ServerNetblock4 partition fail");
PerThread pt;
pt.range4_ = crange;
thr.push_back(pt);
}
}
// IPv6 per-thread partition
if (snb6.defined())
{
IP::RangePartition rp(snb6.clients, n_threads);
IP::Range crange;
for (unsigned int i = 0; i < n_threads; ++i)
{
if (!rp.next(crange))
throw vpn_serv_netblock(opt_name + " : unexpected ServerNetblock6 partition fail");
thr[i].range6_ = crange;
}
}
}
}
const ClientNetblock& netblock4() const { return snb4; }
const ClientNetblock& netblock6() const { return snb6; }
bool netblock_contains(const IP::Addr& a) const
{
return snb4.contains(a) || snb6.contains(a);
}
const size_t size() const { return thr.size(); }
const PerThread& per_thread(const size_t index) const
{
return thr[index];
}
std::string to_string() const
{
std::ostringstream os;
os << "IPv4: " << snb4.to_string() << std::endl;
if (snb6.defined())
os << "IPv6: " << snb6.to_string() << std::endl;
for (size_t i = 0; i < thr.size(); ++i)
{
const PerThread& pt = thr[i];
os << '[' << i << ']';
os << " v4=" << pt.range4().to_string();
if (pt.range6_defined())
os << " v6=" << pt.range6().to_string();
os << std::endl;
}
return os.str();
}
private:
ClientNetblock snb4;
ClientNetblock snb6;
std::vector<PerThread> thr;
};
}
#endif
@@ -0,0 +1,159 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_SERVER_VPNSERVPOOL_H
#define OPENVPN_SERVER_VPNSERVPOOL_H
#include <sstream>
#include <vector>
#include <memory>
#include <mutex>
#include <thread>
#include <cstdint> // for std::uint32_t
#include <openvpn/common/exception.hpp>
#include <openvpn/common/rc.hpp>
#include <openvpn/common/arraysize.hpp>
#include <openvpn/server/vpnservnetblock.hpp>
#include <openvpn/addr/ip.hpp>
#include <openvpn/addr/route.hpp>
#include <openvpn/addr/pool.hpp>
namespace openvpn {
namespace VPNServerPool {
OPENVPN_EXCEPTION(vpn_serv_pool_error);
struct IP46
{
void add_routes(std::vector<IP::Route>& rtvec)
{
if (ip4.defined())
rtvec.emplace_back(ip4, ip4.size());
if (ip6.defined())
rtvec.emplace_back(ip6, ip6.size());
}
std::string to_string() const
{
std::ostringstream os;
os << '[' << ip4 << ' ' << ip6 << ']';
return os.str();
}
bool defined() const
{
return ip4.defined() || ip6.defined();
}
IP::Addr ip4;
IP::Addr ip6;
};
class Pool : public VPNServerNetblock
{
public:
enum Flags {
IPv4_DEPLETION=(1<<0),
IPv6_DEPLETION=(1<<1),
};
Pool(const OptionList& opt)
: VPNServerNetblock(init_snb_from_opt(opt))
{
if (configured(opt, "server"))
{
pool4.add_range(netblock4().clients);
pool6.add_range(netblock6().clients);
}
}
// returns flags
unsigned int acquire(IP46& addr_pair, const bool request_ipv6)
{
std::lock_guard<std::mutex> lock(mutex);
unsigned int flags = 0;
if (!pool4.acquire_addr(addr_pair.ip4))
flags |= IPv4_DEPLETION;
if (request_ipv6 && netblock6().defined())
{
if (!pool6.acquire_addr(addr_pair.ip6))
flags |= IPv6_DEPLETION;
}
return flags;
}
void release(IP46& addr_pair)
{
std::lock_guard<std::mutex> lock(mutex);
if (addr_pair.ip4.defined())
pool4.release_addr(addr_pair.ip4);
if (addr_pair.ip6.defined())
pool6.release_addr(addr_pair.ip6);
}
private:
static VPNServerNetblock init_snb_from_opt(const OptionList& opt)
{
if (configured(opt, "server"))
return VPNServerNetblock(opt, "server", false, 0);
else if (configured(opt, "ifconfig"))
return VPNServerNetblock(opt, "ifconfig", false, 0);
else
throw vpn_serv_pool_error("one of 'server' or 'ifconfig' directives is required");
}
static bool configured(const OptionList& opt,
const std::string& opt_name)
{
return opt.exists(opt_name) || opt.exists(opt_name + "-ipv6");
}
std::mutex mutex;
IP::Pool pool4;
IP::Pool pool6;
};
class IP46AutoRelease : public IP46, public RC<thread_safe_refcount>
{
public:
typedef RCPtr<IP46AutoRelease> Ptr;
IP46AutoRelease(Pool* pool_arg)
: pool(pool_arg)
{
}
~IP46AutoRelease()
{
if (pool)
pool->release(*this);
}
private:
Pool* pool;
};
}
}
#endif