0
0
mirror of https://github.com/OpenVPN/openvpn3.git synced 2024-09-20 12:12:15 +02:00
openvpn3/openvpn/client/cliproto.hpp

1112 lines
31 KiB
C++
Raw Normal View History

// 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_CLIENT_CLIPROTO_H
#define OPENVPN_CLIENT_CLIPROTO_H
// This is a middle-layer object in the OpenVPN client protocol stack.
// It is above the general OpenVPN protocol implementation in
// class ProtoContext but below the top
// level object in a client connect (ClientConnect). See ClientConnect for
// a fuller description of the full client stack.
//
// This layer deals with setting up an OpenVPN client connection:
//
// 1. handles creation of transport-layer handler via TransportClientFactory
// 2. handles creation of tun-layer handler via TunClientFactory
// 3. handles sending PUSH_REQUEST to server and processing reply of server-pushed options
// 4. manages the underlying OpenVPN protocol object (class ProtoContext)
// 5. handles timers on behalf of the underlying OpenVPN protocol object
// 6. acts as an exception dispatcher for errors occuring in any of the underlying layers
#include <string>
#include <vector>
#include <memory>
#include <algorithm> // for std::min
#include <cstdint> // for std::uint...
#include <openvpn/io/io.hpp>
#include <openvpn/common/rc.hpp>
2015-05-17 21:17:24 +02:00
#include <openvpn/common/count.hpp>
#include <openvpn/common/string.hpp>
2016-01-27 09:32:48 +01:00
#include <openvpn/common/base64.hpp>
#include <openvpn/tun/client/tunbase.hpp>
#include <openvpn/transport/client/transbase.hpp>
Added Relay capability, a kind of proxy function similar to HTTP CONNECT but implemented over the OpenVPN protocol. 1. Client connects to relay server as if it were connecting to an ordinary OpenVPN server. 2. Client authenticates to relay server using its client certificate. 3. Client sends a PUSH_REQUEST method to relay server which then replies with a RELAY message instead of PUSH_REPLY. 4. On receiving the RELAY message, the client attempts to reconnect using the existing transport socket. The server will proxy this new connection (at the transport layer) to a second server (chosen by the relay server) that is the target of proxy. 5. The client must establish and authenticate a new session from scratch with the target server, only reusing the transport layer socket from the original connection to the relay server. 6. The relay acts as a man-in-the-middle only at the transport layer (like most proxies), i.e. it forwards the encrypted session between client and target server without decrypting or having the capability to decrypt the session. 7. The client is designed to protect against potentially untrusted or malicious relays: (a) The client never transmits the target server username/password credentials to the relay server. (b) The relay forwards the encrypted OpenVPN session between client and target server without having access to the session keys. (c) The client configuration has a special directive for relay server CA (<relay-extra-ca>) and relay server tls-auth key (<relay-tls-auth>) to allow for separation of TLS/crypto configuration between relay and target servers. (d) The client will reject any PUSH_REPLY messages from the relay itself to prevent the relay from trying to establish a tunnel directly with the client. Example configuring a client for relay: # remote addresses point to the relay server remote ... 1194 udp remote ... 443 tcp # include all other directives for connecting # to the target server # enable relay mode relay-mode # constrain the relay server's cert type relay-ns-cert-type server # include extra CAs that validate the relay # server cert (optional). <relay-extra-ca> -----BEGIN CERTIFICATE----- . . . -----END CERTIFICATE----- </relay-extra-ca> # specify the TLS auth key for the relay server relay-key-direction 1 <relay-tls-auth> -----BEGIN OpenVPN Static key V1----- . . . -----END OpenVPN Static key V1----- </relay-tls-auth>
2016-11-20 21:06:35 +01:00
#include <openvpn/transport/client/relay.hpp>
#include <openvpn/options/continuation.hpp>
#include <openvpn/options/sanitize.hpp>
#include <openvpn/client/clievent.hpp>
#include <openvpn/client/clicreds.hpp>
#include <openvpn/client/cliconstants.hpp>
2012-11-26 02:51:24 +01:00
#include <openvpn/client/clihalt.hpp>
#include <openvpn/time/asiotimer.hpp>
#include <openvpn/time/coarsetime.hpp>
#include <openvpn/time/durhelper.hpp>
2012-11-14 17:41:33 +01:00
#include <openvpn/error/excode.hpp>
#include <openvpn/ssl/proto.hpp>
#ifdef OPENVPN_DEBUG_CLIPROTO
#define OPENVPN_LOG_CLIPROTO(x) OPENVPN_LOG(x)
#else
#define OPENVPN_LOG_CLIPROTO(x)
#endif
namespace openvpn {
namespace ClientProto {
struct NotifyCallback {
virtual void client_proto_terminate() = 0;
virtual void client_proto_connected() {}
};
class Session : ProtoContext,
TransportClientParent,
TunClientParent,
public RC<thread_unsafe_refcount>
{
typedef ProtoContext Base;
typedef Base::PacketType PacketType;
using Base::now;
using Base::stat;
public:
typedef RCPtr<Session> Ptr;
typedef Base::Config ProtoConfig;
OPENVPN_EXCEPTION(client_exception);
Fixed a race condition issue with "hot connect", i.e. sending a connect intent to service when already connected. One of the ramifications of the "hot connect" fix above is that OpenVPNClientBase.is_active() will now return a value that is instantaneously up-to-date, whereas events might lag because of the mechanics of inter-thread message posting. Keep this in mind when correlating received events to is_active() values. For C++ core threads, increased allowed thread-stop delay to 2.5 seconds before thread is marked as unresponsive and abandoned. Previous delay was 1 second. This delay can't be made too long, otherwise Android will tell the user that the app is unresponsive and invite them to kill it. When closing out an abandoned core thread, indicate this condition with a new event type called CORE_THREAD_ABANDONED. If the thread is abandoned due to lack of response to a disconnect request, then the CORE_THREAD_ABANDONED event will occur followed by CORE_THREAD_INACTIVE. For core threads that properly exit, the DISCONNECTED event will be followed by CORE_THREAD_INACTIVE. Added save_as_filename parameter to importProfileRemote method for controlling the filename that the imported profile is saved as. This parameter may be set to null to have the method choose an appropriate name. To have an imported profile replace an existing profile, the filenames much match. Added UI_OVERLOADED debugging constant to OpenVPNClient to allow the UI to connect to a profile when already connected to another profile in order to test "hot connect". Added new events CLIENT_HALT and CLIENT_RESTART for compatibility with an Access Server feature that allows the server to remotely kill or restart the client. When connecting a profile, the core will now automatically fill in the username if it is not specified for userlocked profiles. Version 0.902.
2012-03-31 18:08:20 +02:00
OPENVPN_EXCEPTION(client_halt_restart);
OPENVPN_EXCEPTION(tun_exception);
OPENVPN_EXCEPTION(transport_exception);
OPENVPN_EXCEPTION(max_pushed_options_exceeded);
OPENVPN_SIMPLE_EXCEPTION(session_invalidated);
OPENVPN_SIMPLE_EXCEPTION(authentication_failed);
2013-05-25 03:19:50 +02:00
OPENVPN_SIMPLE_EXCEPTION(inactive_timer_expired);
Added Relay capability, a kind of proxy function similar to HTTP CONNECT but implemented over the OpenVPN protocol. 1. Client connects to relay server as if it were connecting to an ordinary OpenVPN server. 2. Client authenticates to relay server using its client certificate. 3. Client sends a PUSH_REQUEST method to relay server which then replies with a RELAY message instead of PUSH_REPLY. 4. On receiving the RELAY message, the client attempts to reconnect using the existing transport socket. The server will proxy this new connection (at the transport layer) to a second server (chosen by the relay server) that is the target of proxy. 5. The client must establish and authenticate a new session from scratch with the target server, only reusing the transport layer socket from the original connection to the relay server. 6. The relay acts as a man-in-the-middle only at the transport layer (like most proxies), i.e. it forwards the encrypted session between client and target server without decrypting or having the capability to decrypt the session. 7. The client is designed to protect against potentially untrusted or malicious relays: (a) The client never transmits the target server username/password credentials to the relay server. (b) The relay forwards the encrypted OpenVPN session between client and target server without having access to the session keys. (c) The client configuration has a special directive for relay server CA (<relay-extra-ca>) and relay server tls-auth key (<relay-tls-auth>) to allow for separation of TLS/crypto configuration between relay and target servers. (d) The client will reject any PUSH_REPLY messages from the relay itself to prevent the relay from trying to establish a tunnel directly with the client. Example configuring a client for relay: # remote addresses point to the relay server remote ... 1194 udp remote ... 443 tcp # include all other directives for connecting # to the target server # enable relay mode relay-mode # constrain the relay server's cert type relay-ns-cert-type server # include extra CAs that validate the relay # server cert (optional). <relay-extra-ca> -----BEGIN CERTIFICATE----- . . . -----END CERTIFICATE----- </relay-extra-ca> # specify the TLS auth key for the relay server relay-key-direction 1 <relay-tls-auth> -----BEGIN OpenVPN Static key V1----- . . . -----END OpenVPN Static key V1----- </relay-tls-auth>
2016-11-20 21:06:35 +01:00
OPENVPN_SIMPLE_EXCEPTION(relay_event);
OPENVPN_EXCEPTION(proxy_exception);
struct Config : public RC<thread_unsafe_refcount>
{
typedef RCPtr<Config> Ptr;
Config()
: pushed_options_limit("server-pushed options data too large",
ProfileParseLimits::MAX_PUSH_SIZE,
ProfileParseLimits::OPT_OVERHEAD,
ProfileParseLimits::TERM_OVERHEAD,
2012-11-14 03:35:50 +01:00
0,
ProfileParseLimits::MAX_DIRECTIVE_SIZE)
{}
ProtoConfig::Ptr proto_context_config;
ProtoContextOptions::Ptr proto_context_options;
PushOptionsBase::Ptr push_base;
TransportClientFactory::Ptr transport_factory;
TunClientFactory::Ptr tun_factory;
SessionStats::Ptr cli_stats;
ClientEvent::Queue::Ptr cli_events;
ClientCreds::Ptr creds;
OptionList::Limits pushed_options_limit;
OptionList::FilterBase::Ptr pushed_options_filter;
unsigned int tcp_queue_limit = 0;
bool echo = false;
bool info = false;
bool autologin_sessions = false;
};
Session(openvpn_io::io_context& io_context_arg,
const Config& config,
NotifyCallback* notify_callback_arg)
: Base(config.proto_context_config, config.cli_stats),
2015-06-30 08:05:37 +02:00
io_context(io_context_arg),
transport_factory(config.transport_factory),
tun_factory(config.tun_factory),
tcp_queue_limit(config.tcp_queue_limit),
notify_callback(notify_callback_arg),
2015-06-30 08:05:37 +02:00
housekeeping_timer(io_context_arg),
push_request_timer(io_context_arg),
received_options(config.push_base),
creds(config.creds),
proto_context_options(config.proto_context_options),
cli_stats(config.cli_stats),
cli_events(config.cli_events),
echo(config.echo),
info(config.info),
autologin_sessions(config.autologin_sessions),
pushed_options_limit(config.pushed_options_limit),
2013-05-25 03:19:50 +02:00
pushed_options_filter(config.pushed_options_filter),
inactive_timer(io_context_arg),
info_hold_timer(io_context_arg)
{
#ifdef OPENVPN_PACKET_LOG
packet_log.open(OPENVPN_PACKET_LOG, std::ios::binary);
if (!packet_log)
OPENVPN_THROW(open_file_error, "cannot open packet log for output: " << OPENVPN_PACKET_LOG);
#endif
Base::update_now();
Base::reset();
//Base::enable_strict_openvpn_2x();
info_hold.reset(new std::vector<ClientEvent::Base::Ptr>());
}
bool first_packet_received() const { return first_packet_received_; }
void start()
{
if (!halt)
{
Base::update_now();
// coarse wakeup range
housekeeping_schedule.init(Time::Duration::binary_ms(512), Time::Duration::binary_ms(1024));
// initialize transport-layer packet handler
Added Relay capability, a kind of proxy function similar to HTTP CONNECT but implemented over the OpenVPN protocol. 1. Client connects to relay server as if it were connecting to an ordinary OpenVPN server. 2. Client authenticates to relay server using its client certificate. 3. Client sends a PUSH_REQUEST method to relay server which then replies with a RELAY message instead of PUSH_REPLY. 4. On receiving the RELAY message, the client attempts to reconnect using the existing transport socket. The server will proxy this new connection (at the transport layer) to a second server (chosen by the relay server) that is the target of proxy. 5. The client must establish and authenticate a new session from scratch with the target server, only reusing the transport layer socket from the original connection to the relay server. 6. The relay acts as a man-in-the-middle only at the transport layer (like most proxies), i.e. it forwards the encrypted session between client and target server without decrypting or having the capability to decrypt the session. 7. The client is designed to protect against potentially untrusted or malicious relays: (a) The client never transmits the target server username/password credentials to the relay server. (b) The relay forwards the encrypted OpenVPN session between client and target server without having access to the session keys. (c) The client configuration has a special directive for relay server CA (<relay-extra-ca>) and relay server tls-auth key (<relay-tls-auth>) to allow for separation of TLS/crypto configuration between relay and target servers. (d) The client will reject any PUSH_REPLY messages from the relay itself to prevent the relay from trying to establish a tunnel directly with the client. Example configuring a client for relay: # remote addresses point to the relay server remote ... 1194 udp remote ... 443 tcp # include all other directives for connecting # to the target server # enable relay mode relay-mode # constrain the relay server's cert type relay-ns-cert-type server # include extra CAs that validate the relay # server cert (optional). <relay-extra-ca> -----BEGIN CERTIFICATE----- . . . -----END CERTIFICATE----- </relay-extra-ca> # specify the TLS auth key for the relay server relay-key-direction 1 <relay-tls-auth> -----BEGIN OpenVPN Static key V1----- . . . -----END OpenVPN Static key V1----- </relay-tls-auth>
2016-11-20 21:06:35 +01:00
transport = transport_factory->new_transport_client_obj(io_context, this);
transport_has_send_queue = transport->transport_has_send_queue();
Added Relay capability, a kind of proxy function similar to HTTP CONNECT but implemented over the OpenVPN protocol. 1. Client connects to relay server as if it were connecting to an ordinary OpenVPN server. 2. Client authenticates to relay server using its client certificate. 3. Client sends a PUSH_REQUEST method to relay server which then replies with a RELAY message instead of PUSH_REPLY. 4. On receiving the RELAY message, the client attempts to reconnect using the existing transport socket. The server will proxy this new connection (at the transport layer) to a second server (chosen by the relay server) that is the target of proxy. 5. The client must establish and authenticate a new session from scratch with the target server, only reusing the transport layer socket from the original connection to the relay server. 6. The relay acts as a man-in-the-middle only at the transport layer (like most proxies), i.e. it forwards the encrypted session between client and target server without decrypting or having the capability to decrypt the session. 7. The client is designed to protect against potentially untrusted or malicious relays: (a) The client never transmits the target server username/password credentials to the relay server. (b) The relay forwards the encrypted OpenVPN session between client and target server without having access to the session keys. (c) The client configuration has a special directive for relay server CA (<relay-extra-ca>) and relay server tls-auth key (<relay-tls-auth>) to allow for separation of TLS/crypto configuration between relay and target servers. (d) The client will reject any PUSH_REPLY messages from the relay itself to prevent the relay from trying to establish a tunnel directly with the client. Example configuring a client for relay: # remote addresses point to the relay server remote ... 1194 udp remote ... 443 tcp # include all other directives for connecting # to the target server # enable relay mode relay-mode # constrain the relay server's cert type relay-ns-cert-type server # include extra CAs that validate the relay # server cert (optional). <relay-extra-ca> -----BEGIN CERTIFICATE----- . . . -----END CERTIFICATE----- </relay-extra-ca> # specify the TLS auth key for the relay server relay-key-direction 1 <relay-tls-auth> -----BEGIN OpenVPN Static key V1----- . . . -----END OpenVPN Static key V1----- </relay-tls-auth>
2016-11-20 21:06:35 +01:00
if (transport_factory->is_relay())
transport_connecting();
else
transport->transport_start();
}
}
Added Relay capability, a kind of proxy function similar to HTTP CONNECT but implemented over the OpenVPN protocol. 1. Client connects to relay server as if it were connecting to an ordinary OpenVPN server. 2. Client authenticates to relay server using its client certificate. 3. Client sends a PUSH_REQUEST method to relay server which then replies with a RELAY message instead of PUSH_REPLY. 4. On receiving the RELAY message, the client attempts to reconnect using the existing transport socket. The server will proxy this new connection (at the transport layer) to a second server (chosen by the relay server) that is the target of proxy. 5. The client must establish and authenticate a new session from scratch with the target server, only reusing the transport layer socket from the original connection to the relay server. 6. The relay acts as a man-in-the-middle only at the transport layer (like most proxies), i.e. it forwards the encrypted session between client and target server without decrypting or having the capability to decrypt the session. 7. The client is designed to protect against potentially untrusted or malicious relays: (a) The client never transmits the target server username/password credentials to the relay server. (b) The relay forwards the encrypted OpenVPN session between client and target server without having access to the session keys. (c) The client configuration has a special directive for relay server CA (<relay-extra-ca>) and relay server tls-auth key (<relay-tls-auth>) to allow for separation of TLS/crypto configuration between relay and target servers. (d) The client will reject any PUSH_REPLY messages from the relay itself to prevent the relay from trying to establish a tunnel directly with the client. Example configuring a client for relay: # remote addresses point to the relay server remote ... 1194 udp remote ... 443 tcp # include all other directives for connecting # to the target server # enable relay mode relay-mode # constrain the relay server's cert type relay-ns-cert-type server # include extra CAs that validate the relay # server cert (optional). <relay-extra-ca> -----BEGIN CERTIFICATE----- . . . -----END CERTIFICATE----- </relay-extra-ca> # specify the TLS auth key for the relay server relay-key-direction 1 <relay-tls-auth> -----BEGIN OpenVPN Static key V1----- . . . -----END OpenVPN Static key V1----- </relay-tls-auth>
2016-11-20 21:06:35 +01:00
TransportClientFactory::Ptr transport_factory_relay()
{
TransportClient::Ptr tc(new TransportRelayFactory::TransportClientNull(transport.get()));
tc.swap(transport);
return new TransportRelayFactory(io_context, std::move(tc), this);
}
void transport_factory_override(TransportClientFactory::Ptr factory)
{
transport_factory = std::move(factory);
}
2012-02-27 09:16:27 +01:00
void send_explicit_exit_notify()
{
if (!halt)
Base::send_explicit_exit_notify();
}
void tun_set_disconnect()
{
if (tun)
tun->set_disconnect();
}
void post_cc_msg(const std::string& msg)
{
Base::update_now();
Base::write_control_string(msg);
Base::flush(true);
set_housekeeping_timer();
}
void stop(const bool call_terminate_callback)
{
if (!halt)
{
halt = true;
housekeeping_timer.cancel();
push_request_timer.cancel();
2013-05-25 03:19:50 +02:00
inactive_timer.cancel();
info_hold_timer.cancel();
if (notify_callback && call_terminate_callback)
notify_callback->client_proto_terminate();
if (tun)
tun->stop(); // call after client_proto_terminate() so it can call back to tun_set_disconnect
if (transport)
transport->stop();
}
}
void stop_on_signal(const openvpn_io::error_code& error, int signal_number)
{
stop(true);
}
bool reached_connected_state() const { return bool(connected_); }
// If fatal() returns something other than Error::UNDEF, it
// is intended to flag the higher levels (cliconnect.hpp)
// that special handling is required. This handling might include
// considering the error to be fatal and stopping future connect
// retries, or emitting a special event. See cliconnect.hpp
// for actual implementation.
Error::Type fatal() const { return fatal_; }
const std::string& fatal_reason() const { return fatal_reason_; }
2012-02-15 19:19:34 +01:00
virtual ~Session()
{
stop(false);
}
private:
virtual bool transport_is_openvpn_protocol()
{
return true;
}
// transport obj calls here with incoming packets
virtual void transport_recv(BufferAllocated& buf)
{
try {
OPENVPN_LOG_CLIPROTO("Transport RECV " << server_endpoint_render() << ' ' << Base::dump_packet(buf));
// update current time
Base::update_now();
// update last packet received
stat().update_last_packet_received(now());
// log connecting event (only on first packet received)
if (!first_packet_received_)
{
ClientEvent::Base::Ptr ev = new ClientEvent::Connecting();
cli_events->add_event(std::move(ev));
first_packet_received_ = true;
}
// get packet type
Base::PacketType pt = Base::packet_type(buf);
// process packet
if (pt.is_data())
{
// data packet
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 (tun)
{
OPENVPN_LOG_CLIPROTO("TUN send, size=" << buf.size());
tun->tun_send(buf);
}
}
// do a lightweight flush
Base::flush(false);
}
else if (pt.is_control())
{
// control packet
Base::control_net_recv(pt, std::move(buf));
// do a full flush
Base::flush(true);
}
ovpn3 core : Added automatic data limits for Blowfish, Triple DES, and other 64-bit block-size ciphers vulnerable to "Sweet32" birthday attack (CVE-2016-6329). Limit such cipher keys to no more than 64 MB of data encrypted/decrypted. While our overall goal is to limit data-limited keys to 64 MB, we trigger a renegotiation at 48 MB to compensate for possible delays in renegotiation and rollover to the new key. This client-side implementation extends data limit protection to the entire session, even when the server doesn't implement data limits. This capability is advertised to servers via the a peer info setting: IV_BS64DL=1 meaning "Block-Size 64-bit Data Limit". The "1" indicates the implementation version. The implementation currently has some limitations: * Keys are renegotiated at a maximum rate of once per 5 seconds to reduce the likelihood of loss of synchronization between peers. * The maximum renegotiation rate may be further extended if the peer delays rollover from the old to new key after renegotiation. Added N_KEY_LIMIT_RENEG stats counter to count the number of data-limit-triggered renegotiations. Added new stats counter KEY_STATE_ERROR which roughly corresponds to the OpenVPN 2.x error "TLS Error: local/remote TLS keys are out of sync". Prevously, the TLS ack/retransmit timeout was hardcoded to 2 seconds. Now we lower the default to 1 second and make it variable using the (pushable) "tls-timeout" directive. Additionally, the tls-timeout directive can be specified in milliseconds instead of seconds by using the "tls-timeout-ms" form of the directive. Made the "become primary" time duration configurable via the (pushable) "become-primary" directive which accepts a number-of-seconds parameter. become-primary indicates the time delay between renegotiation and rollover to the new key for encryption/transmission. become-primary defaults to the handshake-window which in turn defaults to 60 seconds. Incremented core version to 3.0.20.
2016-09-01 23:19:00 +02:00
else
cli_stats->error(Error::KEY_STATE_ERROR);
// schedule housekeeping wakeup
set_housekeeping_timer();
}
catch (const ExceptionCode& e)
{
if (e.code_defined())
{
if (e.fatal())
transport_error((Error::Type)e.code(), e.what());
else
cli_stats->error((Error::Type)e.code());
}
else
2012-11-14 17:41:33 +01:00
process_exception(e, "transport_recv_excode");
}
catch (const std::exception& e)
{
process_exception(e, "transport_recv");
}
}
virtual void transport_needs_send()
{
}
// tun i/o driver calls here with incoming packets
virtual void tun_recv(BufferAllocated& buf)
{
try {
OPENVPN_LOG_CLIPROTO("TUN recv, size=" << buf.size());
// update current time
Base::update_now();
// log packet
#ifdef OPENVPN_PACKET_LOG
log_packet(buf, true);
#endif
// if transport layer has an output queue, check if it's full
if (transport_has_send_queue)
{
if (transport->transport_send_queue_size() > tcp_queue_limit)
{
buf.reset_size(); // queue full, drop packet
cli_stats->error(Error::TCP_OVERFLOW);
}
}
// encrypt packet
if (buf.size())
{
Base::data_encrypt(buf);
if (buf.size())
{
// send packet via transport to destination
OPENVPN_LOG_CLIPROTO("Transport SEND " << server_endpoint_render() << ' ' << Base::dump_packet(buf));
if (transport->transport_send(buf))
Base::update_last_sent();
else if (halt)
return;
}
}
// do a lightweight flush
Base::flush(false);
// schedule housekeeping wakeup
set_housekeeping_timer();
}
catch (const std::exception& e)
{
2012-07-01 17:37:46 +02:00
process_exception(e, "tun_recv");
}
}
// Return true if keepalive parameter(s) are enabled.
virtual bool is_keepalive_enabled() const
{
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)
{
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();
cli_events->add_event(std::move(ev));
}
std::string server_endpoint_render()
{
std::string server_host, server_port, server_proto, server_ip;
transport->server_endpoint_info(server_host, server_port, server_proto, server_ip);
std::ostringstream out;
out << '[' << server_host << "]:" << server_port << " (" << server_ip << ") via " << server_proto;
return out.str();
}
virtual void transport_wait_proxy()
{
ClientEvent::Base::Ptr ev = new ClientEvent::WaitProxy();
cli_events->add_event(std::move(ev));
}
virtual void transport_wait()
{
ClientEvent::Base::Ptr ev = new ClientEvent::Wait();
cli_events->add_event(std::move(ev));
}
virtual void transport_connecting()
{
try {
OPENVPN_LOG("Connecting to " << server_endpoint_render());
Added Relay capability, a kind of proxy function similar to HTTP CONNECT but implemented over the OpenVPN protocol. 1. Client connects to relay server as if it were connecting to an ordinary OpenVPN server. 2. Client authenticates to relay server using its client certificate. 3. Client sends a PUSH_REQUEST method to relay server which then replies with a RELAY message instead of PUSH_REPLY. 4. On receiving the RELAY message, the client attempts to reconnect using the existing transport socket. The server will proxy this new connection (at the transport layer) to a second server (chosen by the relay server) that is the target of proxy. 5. The client must establish and authenticate a new session from scratch with the target server, only reusing the transport layer socket from the original connection to the relay server. 6. The relay acts as a man-in-the-middle only at the transport layer (like most proxies), i.e. it forwards the encrypted session between client and target server without decrypting or having the capability to decrypt the session. 7. The client is designed to protect against potentially untrusted or malicious relays: (a) The client never transmits the target server username/password credentials to the relay server. (b) The relay forwards the encrypted OpenVPN session between client and target server without having access to the session keys. (c) The client configuration has a special directive for relay server CA (<relay-extra-ca>) and relay server tls-auth key (<relay-tls-auth>) to allow for separation of TLS/crypto configuration between relay and target servers. (d) The client will reject any PUSH_REPLY messages from the relay itself to prevent the relay from trying to establish a tunnel directly with the client. Example configuring a client for relay: # remote addresses point to the relay server remote ... 1194 udp remote ... 443 tcp # include all other directives for connecting # to the target server # enable relay mode relay-mode # constrain the relay server's cert type relay-ns-cert-type server # include extra CAs that validate the relay # server cert (optional). <relay-extra-ca> -----BEGIN CERTIFICATE----- . . . -----END CERTIFICATE----- </relay-extra-ca> # specify the TLS auth key for the relay server relay-key-direction 1 <relay-tls-auth> -----BEGIN OpenVPN Static key V1----- . . . -----END OpenVPN Static key V1----- </relay-tls-auth>
2016-11-20 21:06:35 +01:00
Base::set_protocol(transport->transport_protocol());
Base::start();
Base::flush(true);
set_housekeeping_timer();
}
catch (const std::exception& e)
{
2012-07-01 17:37:46 +02:00
process_exception(e, "transport_connecting");
}
}
virtual void transport_error(const Error::Type fatal_err, const std::string& err_text)
{
if (fatal_err != Error::UNDEF)
{
fatal_ = fatal_err;
fatal_reason_ = err_text;
}
if (notify_callback)
{
OPENVPN_LOG("Transport Error: " << err_text);
stop(true);
}
else
throw transport_exception(err_text);
}
virtual void proxy_error(const Error::Type fatal_err, const std::string& err_text)
{
if (fatal_err != Error::UNDEF)
{
fatal_ = fatal_err;
fatal_reason_ = err_text;
}
if (notify_callback)
{
OPENVPN_LOG("Proxy Error: " << err_text);
stop(true);
}
else
throw proxy_exception(err_text);
}
void extract_auth_token(const OptionList& opt)
{
std::string username;
2016-01-27 09:32:48 +01:00
// auth-token-user
{
const Option* o = opt.get_ptr("auth-token-user");
if (o)
username = base64->decode(o->get(1, 256));
2016-01-27 09:32:48 +01:00
}
// auth-token
{
// if auth-token is present, use it as the password for future renegotiations
const Option* o = opt.get_ptr("auth-token");
if (o)
{
const std::string& sess_id = o->get(1, 256);
if (creds)
{
if (!username.empty())
OPENVPN_LOG("Session user: " << username);
#ifdef OPENVPN_SHOW_SESSION_TOKEN
2016-01-27 09:32:48 +01:00
OPENVPN_LOG("Session token: " << sess_id);
#else
2016-01-27 09:32:48 +01:00
OPENVPN_LOG("Session token: [redacted]");
#endif
creds->set_session_id(username, sess_id);
2016-01-27 09:32:48 +01:00
}
}
}
}
// proto base class calls here for control channel network sends
virtual void control_net_send(const Buffer& net_buf)
{
OPENVPN_LOG_CLIPROTO("Transport SEND " << server_endpoint_render() << ' ' << Base::dump_packet(net_buf));
if (transport->transport_send_const(net_buf))
Base::update_last_sent();
}
// proto base class calls here for app-level control-channel messages received
virtual void control_recv(BufferPtr&& app_bp)
{
2012-11-14 03:35:50 +01:00
const std::string msg = Unicode::utf8_printable(Base::template read_control_string<std::string>(*app_bp),
Unicode::UTF8_FILTER|Unicode::UTF8_PASS_FMT);
2012-11-14 03:35:50 +01:00
//OPENVPN_LOG("SERVER: " << sanitize_control_message(msg));
if (!received_options.complete() && string::starts_with(msg, "PUSH_REPLY,"))
{
// parse the received options
received_options.add(OptionList::parse_from_csv_static(msg.substr(11), &pushed_options_limit),
pushed_options_filter.get());
if (received_options.complete())
{
// show options
OPENVPN_LOG("OPTIONS:" << std::endl << render_options_sanitized(received_options, Option::RENDER_PASS_FMT|Option::RENDER_NUMBER|Option::RENDER_BRACKET));
Added Relay capability, a kind of proxy function similar to HTTP CONNECT but implemented over the OpenVPN protocol. 1. Client connects to relay server as if it were connecting to an ordinary OpenVPN server. 2. Client authenticates to relay server using its client certificate. 3. Client sends a PUSH_REQUEST method to relay server which then replies with a RELAY message instead of PUSH_REPLY. 4. On receiving the RELAY message, the client attempts to reconnect using the existing transport socket. The server will proxy this new connection (at the transport layer) to a second server (chosen by the relay server) that is the target of proxy. 5. The client must establish and authenticate a new session from scratch with the target server, only reusing the transport layer socket from the original connection to the relay server. 6. The relay acts as a man-in-the-middle only at the transport layer (like most proxies), i.e. it forwards the encrypted session between client and target server without decrypting or having the capability to decrypt the session. 7. The client is designed to protect against potentially untrusted or malicious relays: (a) The client never transmits the target server username/password credentials to the relay server. (b) The relay forwards the encrypted OpenVPN session between client and target server without having access to the session keys. (c) The client configuration has a special directive for relay server CA (<relay-extra-ca>) and relay server tls-auth key (<relay-tls-auth>) to allow for separation of TLS/crypto configuration between relay and target servers. (d) The client will reject any PUSH_REPLY messages from the relay itself to prevent the relay from trying to establish a tunnel directly with the client. Example configuring a client for relay: # remote addresses point to the relay server remote ... 1194 udp remote ... 443 tcp # include all other directives for connecting # to the target server # enable relay mode relay-mode # constrain the relay server's cert type relay-ns-cert-type server # include extra CAs that validate the relay # server cert (optional). <relay-extra-ca> -----BEGIN CERTIFICATE----- . . . -----END CERTIFICATE----- </relay-extra-ca> # specify the TLS auth key for the relay server relay-key-direction 1 <relay-tls-auth> -----BEGIN OpenVPN Static key V1----- . . . -----END OpenVPN Static key V1----- </relay-tls-auth>
2016-11-20 21:06:35 +01:00
// relay servers are not allowed to establish a tunnel with us
if (Base::conf().relay_mode)
{
tun_error(Error::RELAY_ERROR, "tunnel not permitted to relay server");
return;
}
// process "echo" directives
if (echo)
process_echo(received_options);
// process auth-token
extract_auth_token(received_options);
// modify proto config (cipher, auth, and compression methods)
Base::process_push(received_options, *proto_context_options);
// initialize tun/routing
2015-06-30 08:05:37 +02:00
tun = tun_factory->new_tun_client_obj(io_context, *this, transport.get());
tun->tun_start(received_options, *transport, Base::dc_settings());
// we should be connected at this point
if (!connected_)
throw tun_exception("not connected");
// initialize data channel after pushed options have been processed
Base::init_data_channel();
// Allow ProtoContext to suggest an alignment adjustment
// hint for transport layer.
transport->reset_align_adjust(Base::align_adjust_hint());
2013-05-25 03:19:50 +02:00
// process "inactive" directive
process_inactive(received_options);
// tell parent that we are connected
if (notify_callback)
notify_callback->client_proto_connected();
// start info-hold timer
schedule_info_hold_callback();
// send the Connected event
cli_events->add_event(connected_);
}
else
OPENVPN_LOG("Options continuation...");
}
else if (string::starts_with(msg, "AUTH_FAILED"))
{
std::string reason;
std::string log_reason;
// get reason (if it exists) for authentication failure
2012-02-15 19:19:34 +01:00
if (msg.length() >= 13)
reason = string::trim_left_copy(std::string(msg, 12));
// If session token problem (such as expiration), and we have a cached
// password, retry with it. Otherwise, fail without retry.
if (string::starts_with(reason, "SESSION:")
&& (autologin_sessions
|| (creds && creds->can_retry_auth_with_cached_password())))
{
log_reason = "SESSION_AUTH_FAILED";
}
else
{
fatal_ = Error::AUTH_FAILED;
fatal_reason_ = reason;
log_reason = "AUTH_FAILED";
}
if (notify_callback)
{
OPENVPN_LOG(log_reason);
stop(true);
}
else
throw authentication_failed();
}
Fixed a race condition issue with "hot connect", i.e. sending a connect intent to service when already connected. One of the ramifications of the "hot connect" fix above is that OpenVPNClientBase.is_active() will now return a value that is instantaneously up-to-date, whereas events might lag because of the mechanics of inter-thread message posting. Keep this in mind when correlating received events to is_active() values. For C++ core threads, increased allowed thread-stop delay to 2.5 seconds before thread is marked as unresponsive and abandoned. Previous delay was 1 second. This delay can't be made too long, otherwise Android will tell the user that the app is unresponsive and invite them to kill it. When closing out an abandoned core thread, indicate this condition with a new event type called CORE_THREAD_ABANDONED. If the thread is abandoned due to lack of response to a disconnect request, then the CORE_THREAD_ABANDONED event will occur followed by CORE_THREAD_INACTIVE. For core threads that properly exit, the DISCONNECTED event will be followed by CORE_THREAD_INACTIVE. Added save_as_filename parameter to importProfileRemote method for controlling the filename that the imported profile is saved as. This parameter may be set to null to have the method choose an appropriate name. To have an imported profile replace an existing profile, the filenames much match. Added UI_OVERLOADED debugging constant to OpenVPNClient to allow the UI to connect to a profile when already connected to another profile in order to test "hot connect". Added new events CLIENT_HALT and CLIENT_RESTART for compatibility with an Access Server feature that allows the server to remotely kill or restart the client. When connecting a profile, the core will now automatically fill in the username if it is not specified for userlocked profiles. Version 0.902.
2012-03-31 18:08:20 +02:00
else if (ClientHalt::match(msg))
{
const ClientHalt ch(msg, true);
Fixed a race condition issue with "hot connect", i.e. sending a connect intent to service when already connected. One of the ramifications of the "hot connect" fix above is that OpenVPNClientBase.is_active() will now return a value that is instantaneously up-to-date, whereas events might lag because of the mechanics of inter-thread message posting. Keep this in mind when correlating received events to is_active() values. For C++ core threads, increased allowed thread-stop delay to 2.5 seconds before thread is marked as unresponsive and abandoned. Previous delay was 1 second. This delay can't be made too long, otherwise Android will tell the user that the app is unresponsive and invite them to kill it. When closing out an abandoned core thread, indicate this condition with a new event type called CORE_THREAD_ABANDONED. If the thread is abandoned due to lack of response to a disconnect request, then the CORE_THREAD_ABANDONED event will occur followed by CORE_THREAD_INACTIVE. For core threads that properly exit, the DISCONNECTED event will be followed by CORE_THREAD_INACTIVE. Added save_as_filename parameter to importProfileRemote method for controlling the filename that the imported profile is saved as. This parameter may be set to null to have the method choose an appropriate name. To have an imported profile replace an existing profile, the filenames much match. Added UI_OVERLOADED debugging constant to OpenVPNClient to allow the UI to connect to a profile when already connected to another profile in order to test "hot connect". Added new events CLIENT_HALT and CLIENT_RESTART for compatibility with an Access Server feature that allows the server to remotely kill or restart the client. When connecting a profile, the core will now automatically fill in the username if it is not specified for userlocked profiles. Version 0.902.
2012-03-31 18:08:20 +02:00
process_halt_restart(ch);
}
else if (info && string::starts_with(msg, "INFO,"))
{
// Buffer INFO messages received near Connected event to fire
// one second after Connected event, to reduce the chance of
// race conditions in the client app, if the INFO event
// triggers the client app to perform an operation that
// requires the VPN tunnel to be ready.
ClientEvent::Base::Ptr ev = new ClientEvent::Info(msg.substr(5));
if (info_hold)
info_hold->push_back(std::move(ev));
else
cli_events->add_event(std::move(ev));
}
Implemented client-side AUTH_PENDING protocol state AUTH_PENDING is a control channel message sent from server to client before PUSH_REPLY or AUTH_FAILED and is intended to signal the client that a browser-based out-of-band authentication challenge (such as SAML) needs to occur before the connection request can succeed or fail. When the core receives the AUTH_PENDING message, it will enter the AUTH_PENDING state and forward the message to the client UI as an event. The core will also dial back the PUSH_REQUEST transmit frequency to one message every 8 seconds, and the server is expected to reply with an AUTH_PENDING message after every PUSH_REQUEST. This is done as a sort of keepalive replacement since the normal OpenVPN protocol keepalive functionality isn't enabled until the crypto state is established, which doesn't happen until the PUSH_REPLY message is received from the server. During the AUTH_PENDING state, the server will likely want to push INFO messages to the client UI (such as INFO,OPEN_URL:) to facilitate the out-of-band authentication challenge. Normally, the client core buffers early INFO messages and doesn't release them to the UI until 1 second after the CONNECTED event. This is done because it was presumed that the server wouldn't want the client to act on the INFO messages until the tunnel is established. But the AUTH_PENDING state creates a need for an unbuffered INFO message, since the server may want to message the client UI during the AUTH_PENDING state and have that message be immediately processed. I've solved this problem by introducing a new control channel message called "INFO_PRE". INFO_PRE is handled exactly the same as INFO except it is never buffered. Also, note that INFO_PRE messages are delivered to the client UI as ordinary INFO events (I didn't actually create a new client event for INFO_PRE since I can't think of a reason why the client UI would need to distinguish between them). Signed-off-by: James Yonan <james@openvpn.net>
2018-04-12 22:32:14 +02:00
else if (info && string::starts_with(msg, "INFO_PRE,"))
{
// INFO_PRE is like INFO but it is never buffered
ClientEvent::Base::Ptr ev = new ClientEvent::Info(msg.substr(9));
cli_events->add_event(std::move(ev));
}
else if (msg == "AUTH_PENDING")
{
// AUTH_PENDING indicates an out-of-band authentication step must
// be performed before the server will send the PUSH_REPLY message.
if (!auth_pending)
{
auth_pending = true;
ClientEvent::Base::Ptr ev = new ClientEvent::AuthPending();
cli_events->add_event(std::move(ev));
}
}
Added Relay capability, a kind of proxy function similar to HTTP CONNECT but implemented over the OpenVPN protocol. 1. Client connects to relay server as if it were connecting to an ordinary OpenVPN server. 2. Client authenticates to relay server using its client certificate. 3. Client sends a PUSH_REQUEST method to relay server which then replies with a RELAY message instead of PUSH_REPLY. 4. On receiving the RELAY message, the client attempts to reconnect using the existing transport socket. The server will proxy this new connection (at the transport layer) to a second server (chosen by the relay server) that is the target of proxy. 5. The client must establish and authenticate a new session from scratch with the target server, only reusing the transport layer socket from the original connection to the relay server. 6. The relay acts as a man-in-the-middle only at the transport layer (like most proxies), i.e. it forwards the encrypted session between client and target server without decrypting or having the capability to decrypt the session. 7. The client is designed to protect against potentially untrusted or malicious relays: (a) The client never transmits the target server username/password credentials to the relay server. (b) The relay forwards the encrypted OpenVPN session between client and target server without having access to the session keys. (c) The client configuration has a special directive for relay server CA (<relay-extra-ca>) and relay server tls-auth key (<relay-tls-auth>) to allow for separation of TLS/crypto configuration between relay and target servers. (d) The client will reject any PUSH_REPLY messages from the relay itself to prevent the relay from trying to establish a tunnel directly with the client. Example configuring a client for relay: # remote addresses point to the relay server remote ... 1194 udp remote ... 443 tcp # include all other directives for connecting # to the target server # enable relay mode relay-mode # constrain the relay server's cert type relay-ns-cert-type server # include extra CAs that validate the relay # server cert (optional). <relay-extra-ca> -----BEGIN CERTIFICATE----- . . . -----END CERTIFICATE----- </relay-extra-ca> # specify the TLS auth key for the relay server relay-key-direction 1 <relay-tls-auth> -----BEGIN OpenVPN Static key V1----- . . . -----END OpenVPN Static key V1----- </relay-tls-auth>
2016-11-20 21:06:35 +01:00
else if (msg == "RELAY")
{
if (Base::conf().relay_mode)
{
fatal_ = Error::RELAY;
fatal_reason_ = "";
}
else
{
fatal_ = Error::RELAY_ERROR;
fatal_reason_ = "not in relay mode";
}
if (notify_callback)
{
OPENVPN_LOG(Error::name(fatal_) << ' ' << fatal_reason_);
stop(true);
}
else
throw relay_event();
}
}
virtual void tun_pre_tun_config()
{
ClientEvent::Base::Ptr ev = new ClientEvent::AssignIP();
cli_events->add_event(std::move(ev));
}
virtual void tun_pre_route_config()
{
ClientEvent::Base::Ptr ev = new ClientEvent::AddRoutes();
cli_events->add_event(std::move(ev));
}
virtual void tun_connected()
{
OPENVPN_LOG("Connected via " + tun->tun_name());
ClientEvent::Connected::Ptr ev = new ClientEvent::Connected();
if (creds)
ev->user = creds->get_username();
transport->server_endpoint_info(ev->server_host, ev->server_port, ev->server_proto, ev->server_ip);
ev->vpn_ip4 = tun->vpn_ip4();
ev->vpn_ip6 = tun->vpn_ip6();
ev->vpn_gw4 = tun->vpn_gw4();
ev->vpn_gw6 = tun->vpn_gw6();
try {
2012-11-14 03:35:50 +01:00
std::string client_ip = received_options.get_optional("client-ip", 1, 256);
if (!client_ip.empty())
ev->client_ip = IP::Addr::validate(client_ip, "client-ip");
}
catch (const std::exception& e)
{
OPENVPN_LOG("exception parsing client-ip: " << e.what());
}
ev->tun_name = tun->tun_name();
connected_ = std::move(ev);
}
virtual void tun_error(const Error::Type fatal_err, const std::string& err_text)
{
if (fatal_err != Error::UNDEF)
{
fatal_ = fatal_err;
fatal_reason_ = err_text;
}
if (notify_callback)
{
OPENVPN_LOG("TUN Error: " << err_text);
stop(true);
}
else
throw tun_exception(err_text);
}
// proto base class calls here to get auth credentials
virtual void client_auth(Buffer& buf)
{
Added Relay capability, a kind of proxy function similar to HTTP CONNECT but implemented over the OpenVPN protocol. 1. Client connects to relay server as if it were connecting to an ordinary OpenVPN server. 2. Client authenticates to relay server using its client certificate. 3. Client sends a PUSH_REQUEST method to relay server which then replies with a RELAY message instead of PUSH_REPLY. 4. On receiving the RELAY message, the client attempts to reconnect using the existing transport socket. The server will proxy this new connection (at the transport layer) to a second server (chosen by the relay server) that is the target of proxy. 5. The client must establish and authenticate a new session from scratch with the target server, only reusing the transport layer socket from the original connection to the relay server. 6. The relay acts as a man-in-the-middle only at the transport layer (like most proxies), i.e. it forwards the encrypted session between client and target server without decrypting or having the capability to decrypt the session. 7. The client is designed to protect against potentially untrusted or malicious relays: (a) The client never transmits the target server username/password credentials to the relay server. (b) The relay forwards the encrypted OpenVPN session between client and target server without having access to the session keys. (c) The client configuration has a special directive for relay server CA (<relay-extra-ca>) and relay server tls-auth key (<relay-tls-auth>) to allow for separation of TLS/crypto configuration between relay and target servers. (d) The client will reject any PUSH_REPLY messages from the relay itself to prevent the relay from trying to establish a tunnel directly with the client. Example configuring a client for relay: # remote addresses point to the relay server remote ... 1194 udp remote ... 443 tcp # include all other directives for connecting # to the target server # enable relay mode relay-mode # constrain the relay server's cert type relay-ns-cert-type server # include extra CAs that validate the relay # server cert (optional). <relay-extra-ca> -----BEGIN CERTIFICATE----- . . . -----END CERTIFICATE----- </relay-extra-ca> # specify the TLS auth key for the relay server relay-key-direction 1 <relay-tls-auth> -----BEGIN OpenVPN Static key V1----- . . . -----END OpenVPN Static key V1----- </relay-tls-auth>
2016-11-20 21:06:35 +01:00
// we never send creds to a relay server
if (creds && !Base::conf().relay_mode)
{
OPENVPN_LOG("Creds: " << creds->auth_info());
Base::write_auth_string(creds->get_username(), buf);
Base::write_auth_string(creds->get_password(), buf);
}
else
{
OPENVPN_LOG("Creds: None");
Base::write_empty_string(buf); // username
Base::write_empty_string(buf); // password
}
}
void send_push_request_callback(const Time::Duration& dur,
const openvpn_io::error_code& e)
{
try {
if (!e && !halt && !received_options.partial())
{
Base::update_now();
if (!sent_push_request)
{
ClientEvent::Base::Ptr ev = new ClientEvent::GetConfig();
cli_events->add_event(std::move(ev));
sent_push_request = true;
}
OPENVPN_LOG("Sending PUSH_REQUEST to server...");
Base::write_control_string(std::string("PUSH_REQUEST"));
Base::flush(true);
set_housekeeping_timer();
{
Implemented client-side AUTH_PENDING protocol state AUTH_PENDING is a control channel message sent from server to client before PUSH_REPLY or AUTH_FAILED and is intended to signal the client that a browser-based out-of-band authentication challenge (such as SAML) needs to occur before the connection request can succeed or fail. When the core receives the AUTH_PENDING message, it will enter the AUTH_PENDING state and forward the message to the client UI as an event. The core will also dial back the PUSH_REQUEST transmit frequency to one message every 8 seconds, and the server is expected to reply with an AUTH_PENDING message after every PUSH_REQUEST. This is done as a sort of keepalive replacement since the normal OpenVPN protocol keepalive functionality isn't enabled until the crypto state is established, which doesn't happen until the PUSH_REPLY message is received from the server. During the AUTH_PENDING state, the server will likely want to push INFO messages to the client UI (such as INFO,OPEN_URL:) to facilitate the out-of-band authentication challenge. Normally, the client core buffers early INFO messages and doesn't release them to the UI until 1 second after the CONNECTED event. This is done because it was presumed that the server wouldn't want the client to act on the INFO messages until the tunnel is established. But the AUTH_PENDING state creates a need for an unbuffered INFO message, since the server may want to message the client UI during the AUTH_PENDING state and have that message be immediately processed. I've solved this problem by introducing a new control channel message called "INFO_PRE". INFO_PRE is handled exactly the same as INFO except it is never buffered. Also, note that INFO_PRE messages are delivered to the client UI as ordinary INFO events (I didn't actually create a new client event for INFO_PRE since I can't think of a reason why the client UI would need to distinguish between them). Signed-off-by: James Yonan <james@openvpn.net>
2018-04-12 22:32:14 +02:00
if (auth_pending)
{
// With auth_pending, we can dial back the PUSH_REQUEST
// frequency, but we still need back-and-forth network
// activity to avoid an inactivity timeout, since the crypto
// layer (and hence keepalive ping) is not initialized until
// we receive the PUSH_REPLY from the server.
schedule_push_request_callback(Time::Duration::seconds(8));
}
else
{
// step function with ceiling: 1 sec, 2 secs, 3 secs, 3, 3, ...
const Time::Duration newdur = std::min(dur + Time::Duration::seconds(1),
Time::Duration::seconds(3));
schedule_push_request_callback(newdur);
}
}
}
}
catch (const std::exception& e)
{
2012-07-01 17:37:46 +02:00
process_exception(e, "send_push_request_callback");
}
}
void schedule_push_request_callback(const Time::Duration& dur)
{
if (!received_options.partial())
{
push_request_timer.expires_after(dur);
push_request_timer.async_wait([self=Ptr(this), dur](const openvpn_io::error_code& error)
{
OPENVPN_ASYNC_HANDLER;
self->send_push_request_callback(dur, error);
});
}
}
// react to any tls warning triggered during the tls-handshake
virtual void check_tls_warnings()
{
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));
}
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
virtual void active()
{
OPENVPN_LOG("Session is ACTIVE");
check_tls_warnings();
schedule_push_request_callback(Time::Duration::seconds(0));
}
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())
{
if (notify_callback)
{
OPENVPN_LOG("Session invalidated: " << Error::name(Base::invalidation_reason()));
stop(true);
}
else
throw session_invalidated();
}
set_housekeeping_timer();
}
}
catch (const std::exception& e)
{
2012-07-01 17:37:46 +02:00
process_exception(e, "housekeeping_callback");
}
}
void set_housekeeping_timer()
{
if (halt)
return;
Time next = Base::next_housekeeping();
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)
{
OPENVPN_ASYNC_HANDLER;
self->housekeeping_callback(error);
});
}
else
{
housekeeping_timer.cancel();
housekeeping_schedule.reset();
}
}
}
void process_inactive(const OptionList& opt)
2013-05-25 03:19:50 +02:00
{
try {
ovpn3 core : Added automatic data limits for Blowfish, Triple DES, and other 64-bit block-size ciphers vulnerable to "Sweet32" birthday attack (CVE-2016-6329). Limit such cipher keys to no more than 64 MB of data encrypted/decrypted. While our overall goal is to limit data-limited keys to 64 MB, we trigger a renegotiation at 48 MB to compensate for possible delays in renegotiation and rollover to the new key. This client-side implementation extends data limit protection to the entire session, even when the server doesn't implement data limits. This capability is advertised to servers via the a peer info setting: IV_BS64DL=1 meaning "Block-Size 64-bit Data Limit". The "1" indicates the implementation version. The implementation currently has some limitations: * Keys are renegotiated at a maximum rate of once per 5 seconds to reduce the likelihood of loss of synchronization between peers. * The maximum renegotiation rate may be further extended if the peer delays rollover from the old to new key after renegotiation. Added N_KEY_LIMIT_RENEG stats counter to count the number of data-limit-triggered renegotiations. Added new stats counter KEY_STATE_ERROR which roughly corresponds to the OpenVPN 2.x error "TLS Error: local/remote TLS keys are out of sync". Prevously, the TLS ack/retransmit timeout was hardcoded to 2 seconds. Now we lower the default to 1 second and make it variable using the (pushable) "tls-timeout" directive. Additionally, the tls-timeout directive can be specified in milliseconds instead of seconds by using the "tls-timeout-ms" form of the directive. Made the "become primary" time duration configurable via the (pushable) "become-primary" directive which accepts a number-of-seconds parameter. become-primary indicates the time delay between renegotiation and rollover to the new key for encryption/transmission. become-primary defaults to the handshake-window which in turn defaults to 60 seconds. Incremented core version to 3.0.20.
2016-09-01 23:19:00 +02:00
const Option *o = load_duration_parm(inactive_duration, "inactive", opt, 1, false, false);
2013-05-25 03:19:50 +02:00
if (o)
{
if (o->size() >= 3)
inactive_bytes = parse_number_throw<unsigned int>(o->get(2, 16), "inactive bytes");
schedule_inactive_timer();
}
}
catch (const std::exception& e)
{
OPENVPN_LOG("exception parsing inactive: " << e.what());
2013-05-25 03:19:50 +02:00
}
}
void schedule_inactive_timer()
{
inactive_timer.expires_after(inactive_duration);
inactive_timer.async_wait([self=Ptr(this)](const openvpn_io::error_code& error)
{
OPENVPN_ASYNC_HANDLER;
self->inactive_callback(error);
});
2013-05-25 03:19:50 +02:00
}
void inactive_callback(const openvpn_io::error_code& e) // fixme for DCO
2013-05-25 03:19:50 +02:00
{
try {
if (!e && !halt)
{
// update current time
Base::update_now();
const count_t sample = cli_stats->get_stat(SessionStats::TUN_BYTES_IN) + cli_stats->get_stat(SessionStats::TUN_BYTES_OUT);
const count_t delta = sample - inactive_last_sample;
//OPENVPN_LOG("*** INACTIVE SAMPLE " << delta << ' ' << inactive_bytes);
if (delta <= count_t(inactive_bytes))
{
fatal_ = Error::INACTIVE_TIMEOUT;
send_explicit_exit_notify();
if (notify_callback)
{
OPENVPN_LOG("inactive timer expired");
stop(true);
}
else
throw inactive_timer_expired();
}
else
{
inactive_last_sample = sample;
schedule_inactive_timer();
}
}
}
catch (const std::exception& e)
{
process_exception(e, "inactive_callback");
}
}
void process_echo(const OptionList& opt)
{
OptionList::IndexMap::const_iterator echo_opt = opt.map().find("echo");
if (echo_opt != opt.map().end())
{
for (OptionList::IndexList::const_iterator i = echo_opt->second.begin(); i != echo_opt->second.end(); ++i)
{
const Option& o = opt[*i];
o.touch();
const std::string& value = o.get(1, 512);
ClientEvent::Base::Ptr ev = new ClientEvent::Echo(value);
cli_events->add_event(std::move(ev));
}
}
}
2012-07-01 17:37:46 +02:00
void process_exception(const std::exception& e, const char *method_name)
{
if (notify_callback)
{
2012-07-01 17:37:46 +02:00
OPENVPN_LOG("Client exception in " << method_name << ": " << e.what());
stop(true);
}
else
throw client_exception(e.what());
}
Fixed a race condition issue with "hot connect", i.e. sending a connect intent to service when already connected. One of the ramifications of the "hot connect" fix above is that OpenVPNClientBase.is_active() will now return a value that is instantaneously up-to-date, whereas events might lag because of the mechanics of inter-thread message posting. Keep this in mind when correlating received events to is_active() values. For C++ core threads, increased allowed thread-stop delay to 2.5 seconds before thread is marked as unresponsive and abandoned. Previous delay was 1 second. This delay can't be made too long, otherwise Android will tell the user that the app is unresponsive and invite them to kill it. When closing out an abandoned core thread, indicate this condition with a new event type called CORE_THREAD_ABANDONED. If the thread is abandoned due to lack of response to a disconnect request, then the CORE_THREAD_ABANDONED event will occur followed by CORE_THREAD_INACTIVE. For core threads that properly exit, the DISCONNECTED event will be followed by CORE_THREAD_INACTIVE. Added save_as_filename parameter to importProfileRemote method for controlling the filename that the imported profile is saved as. This parameter may be set to null to have the method choose an appropriate name. To have an imported profile replace an existing profile, the filenames much match. Added UI_OVERLOADED debugging constant to OpenVPNClient to allow the UI to connect to a profile when already connected to another profile in order to test "hot connect". Added new events CLIENT_HALT and CLIENT_RESTART for compatibility with an Access Server feature that allows the server to remotely kill or restart the client. When connecting a profile, the core will now automatically fill in the username if it is not specified for userlocked profiles. Version 0.902.
2012-03-31 18:08:20 +02:00
void process_halt_restart(const ClientHalt& ch)
{
if (!ch.psid() && creds)
creds->can_retry_auth_with_cached_password(); // purge session ID
if (ch.restart())
Fixed a race condition issue with "hot connect", i.e. sending a connect intent to service when already connected. One of the ramifications of the "hot connect" fix above is that OpenVPNClientBase.is_active() will now return a value that is instantaneously up-to-date, whereas events might lag because of the mechanics of inter-thread message posting. Keep this in mind when correlating received events to is_active() values. For C++ core threads, increased allowed thread-stop delay to 2.5 seconds before thread is marked as unresponsive and abandoned. Previous delay was 1 second. This delay can't be made too long, otherwise Android will tell the user that the app is unresponsive and invite them to kill it. When closing out an abandoned core thread, indicate this condition with a new event type called CORE_THREAD_ABANDONED. If the thread is abandoned due to lack of response to a disconnect request, then the CORE_THREAD_ABANDONED event will occur followed by CORE_THREAD_INACTIVE. For core threads that properly exit, the DISCONNECTED event will be followed by CORE_THREAD_INACTIVE. Added save_as_filename parameter to importProfileRemote method for controlling the filename that the imported profile is saved as. This parameter may be set to null to have the method choose an appropriate name. To have an imported profile replace an existing profile, the filenames much match. Added UI_OVERLOADED debugging constant to OpenVPNClient to allow the UI to connect to a profile when already connected to another profile in order to test "hot connect". Added new events CLIENT_HALT and CLIENT_RESTART for compatibility with an Access Server feature that allows the server to remotely kill or restart the client. When connecting a profile, the core will now automatically fill in the username if it is not specified for userlocked profiles. Version 0.902.
2012-03-31 18:08:20 +02:00
fatal_ = Error::CLIENT_RESTART;
else
fatal_ = Error::CLIENT_HALT;
fatal_reason_ = ch.reason();
if (notify_callback)
{
OPENVPN_LOG("Client halt/restart: " << ch.render());
stop(true);
}
else
throw client_halt_restart(ch.render());
}
void schedule_info_hold_callback()
{
Base::update_now();
info_hold_timer.expires_after(Time::Duration::seconds(1));
info_hold_timer.async_wait([self=Ptr(this)](const openvpn_io::error_code& error)
{
OPENVPN_ASYNC_HANDLER;
self->info_hold_callback(error);
});
}
void info_hold_callback(const openvpn_io::error_code& e)
{
try {
if (!e && !halt)
{
Base::update_now();
if (info_hold)
{
for (auto &ev : *info_hold)
cli_events->add_event(std::move(ev));
info_hold.reset();
}
}
}
catch (const std::exception& e)
{
process_exception(e, "info_hold_callback");
}
}
#ifdef OPENVPN_PACKET_LOG
void log_packet(const Buffer& buf, const bool out)
{
if (buf.size())
{
std::uint16_t len = buf.size() & 0x7FFF;
if (out)
len |= 0x8000;
packet_log.write((const char *)&len, sizeof(len));
packet_log.write((const char *)buf.c_data(), buf.size());
}
}
#endif
openvpn_io::io_context& io_context;
TransportClientFactory::Ptr transport_factory;
TransportClient::Ptr transport;
TunClientFactory::Ptr tun_factory;
TunClient::Ptr tun;
unsigned int tcp_queue_limit;
bool transport_has_send_queue = false;
NotifyCallback* notify_callback;
CoarseTime housekeeping_schedule;
AsioTimer housekeeping_timer;
AsioTimer push_request_timer;
bool halt = false;
OptionListContinuation received_options;
ClientCreds::Ptr creds;
ProtoContextOptions::Ptr proto_context_options;
iOS version: 1.0 Beta 17 Android version: 1.1 beta 1 More alignment of iOS and Android clients: * Normalized building of dependencies for Android and iOS: This build adds some new library dependencies: The library versions required are enumerated in ovpn3/lib-versions, currently: export BOOST_VERSION=boost_1_51_0 export OPENSSL_VERSION=openssl-1.0.1c export POLARSSL_VERSION=polarssl-1.1.4 export LZO_VERSION=lzo-2.06 To build, first mkdir ~/src/android and ~/src/mac if they don't already exist. Set the env var O3 to point to the ovpn3 dir, usually ~/src/ovpn3. Build on iOS: [set PATH to include NDK] cd ~/src/android $O3/scripts/android/build-boost $O3/scripts/android/build-minicrypto $O3/scripts/android/build-polarssl $O3/scripts/android/build-lzo Build on Android: [set PATH to include NDK] cd ~/src/android $O3/scripts/android/build-boost $O3/scripts/android/build-minicrypto $O3/scripts/android/build-polarssl $O3/scripts/android/build-lzo * Integrated Minicrypto library (an assembly language library of low-level crypto functions adapted from OpenSSL). * Added LZO compression with a preference/settings item to enable or disable. * Added special compression handling to support older servers that ignore compression handshake -- this will handle receiving compressed packets even if we didn't ask for them. * Normalized profile naming conventions. iOS changes: * Log tunnel performance stats immediately on disconnection of tunnel. Android changes: * Client now supports loading profiles as attachments opened from other apps. * Added Import Private Tunnel menu item, however current Private Tunnel download page needs to be adapted to fit requirements of Android download manager. * Enter key should advance to the next input field, or connect if entered from the last field. * Import from Access Server now provides the option to download autologin vs. userlogin profiles. * "About" page now shows copyright text for included libraries/content (except for LZO and PolarSSL which will presumably be commercially licensed).
2012-09-05 03:09:34 +02:00
bool first_packet_received_ = false;
bool sent_push_request = false;
Implemented client-side AUTH_PENDING protocol state AUTH_PENDING is a control channel message sent from server to client before PUSH_REPLY or AUTH_FAILED and is intended to signal the client that a browser-based out-of-band authentication challenge (such as SAML) needs to occur before the connection request can succeed or fail. When the core receives the AUTH_PENDING message, it will enter the AUTH_PENDING state and forward the message to the client UI as an event. The core will also dial back the PUSH_REQUEST transmit frequency to one message every 8 seconds, and the server is expected to reply with an AUTH_PENDING message after every PUSH_REQUEST. This is done as a sort of keepalive replacement since the normal OpenVPN protocol keepalive functionality isn't enabled until the crypto state is established, which doesn't happen until the PUSH_REPLY message is received from the server. During the AUTH_PENDING state, the server will likely want to push INFO messages to the client UI (such as INFO,OPEN_URL:) to facilitate the out-of-band authentication challenge. Normally, the client core buffers early INFO messages and doesn't release them to the UI until 1 second after the CONNECTED event. This is done because it was presumed that the server wouldn't want the client to act on the INFO messages until the tunnel is established. But the AUTH_PENDING state creates a need for an unbuffered INFO message, since the server may want to message the client UI during the AUTH_PENDING state and have that message be immediately processed. I've solved this problem by introducing a new control channel message called "INFO_PRE". INFO_PRE is handled exactly the same as INFO except it is never buffered. Also, note that INFO_PRE messages are delivered to the client UI as ordinary INFO events (I didn't actually create a new client event for INFO_PRE since I can't think of a reason why the client UI would need to distinguish between them). Signed-off-by: James Yonan <james@openvpn.net>
2018-04-12 22:32:14 +02:00
bool auth_pending = false;
SessionStats::Ptr cli_stats;
ClientEvent::Queue::Ptr cli_events;
ClientEvent::Connected::Ptr connected_;
bool echo;
bool info;
bool autologin_sessions;
Error::Type fatal_ = Error::UNDEF;
std::string fatal_reason_;
OptionList::Limits pushed_options_limit;
OptionList::FilterBase::Ptr pushed_options_filter;
2013-05-25 03:19:50 +02:00
AsioTimer inactive_timer;
Time::Duration inactive_duration;
unsigned int inactive_bytes = 0;
count_t inactive_last_sample = 0;
2013-05-25 03:19:50 +02:00
std::unique_ptr<std::vector<ClientEvent::Base::Ptr>> info_hold;
AsioTimer info_hold_timer;
#ifdef OPENVPN_PACKET_LOG
std::ofstream packet_log;
#endif
};
}
}
#endif