mirror of
https://github.com/OpenVPN/openvpn3.git
synced 2024-09-19 19:52:15 +02:00
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.
This commit is contained in:
parent
26d0169055
commit
662bf7833e
@ -296,6 +296,8 @@ namespace openvpn {
|
||||
// do a full flush
|
||||
Base::flush(true);
|
||||
}
|
||||
else
|
||||
cli_stats->error(Error::KEY_STATE_ERROR);
|
||||
|
||||
// schedule housekeeping wakeup
|
||||
set_housekeeping_timer();
|
||||
@ -783,7 +785,7 @@ namespace openvpn {
|
||||
void process_inactive(const OptionList& opt)
|
||||
{
|
||||
try {
|
||||
const Option *o = load_duration_parm(inactive_duration, "inactive", opt, 1, false);
|
||||
const Option *o = load_duration_parm(inactive_duration, "inactive", opt, 1, false, false);
|
||||
if (o)
|
||||
{
|
||||
if (o->size() >= 3)
|
||||
|
@ -24,6 +24,6 @@
|
||||
#ifndef OPENVPN_COMMON_VERSION_H
|
||||
#define OPENVPN_COMMON_VERSION_H
|
||||
|
||||
#define OPENVPN_VERSION "3.0.19"
|
||||
#define OPENVPN_VERSION "3.0.20"
|
||||
|
||||
#endif // OPENVPN_COMMON_VERSION_H
|
||||
|
45
openvpn/crypto/bs64_data_limit.hpp
Normal file
45
openvpn/crypto/bs64_data_limit.hpp
Normal file
@ -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-2015 OpenVPN Technologies, 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/>.
|
||||
|
||||
// Special data limits on 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. Note that we trigger early at
|
||||
// 48 MB to compensate for possible delays in renegotiation and
|
||||
// rollover to the new key.
|
||||
|
||||
#ifndef OPENVPN_CRYPTO_DATALIMIT_H
|
||||
#define OPENVPN_CRYPTO_DATALIMIT_H
|
||||
|
||||
#include <openvpn/crypto/cryptoalgs.hpp>
|
||||
|
||||
#ifndef OPENVPN_BS64_DATA_LIMIT
|
||||
#define OPENVPN_BS64_DATA_LIMIT 48000000
|
||||
#endif
|
||||
|
||||
namespace openvpn {
|
||||
inline bool is_bs64_cipher(const CryptoAlgs::Type cipher)
|
||||
{
|
||||
return CryptoAlgs::get(cipher).block_size() == 8;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -77,12 +77,15 @@ namespace openvpn {
|
||||
CLIENT_RESTART, // RESTART message from server received
|
||||
N_PAUSE, // Number of transitions to Pause state
|
||||
N_RECONNECT, // Number of reconnections
|
||||
N_KEY_LIMIT_RENEG, // Number of renegotiations triggered by per-key limits such as data or packet limits
|
||||
KEY_STATE_ERROR, // Received packet didn't match expected key state
|
||||
PROXY_ERROR, // HTTP proxy error
|
||||
PROXY_NEED_CREDS, // HTTP proxy needs credentials
|
||||
|
||||
// key event errors
|
||||
KEV_NEGOTIATE_ERROR,
|
||||
KEV_EXPIRE_ERROR,
|
||||
KEV_PENDING_ERROR,
|
||||
N_KEV_EXPIRE,
|
||||
|
||||
// Packet ID error detail
|
||||
PKTID_INVALID,
|
||||
@ -147,10 +150,13 @@ namespace openvpn {
|
||||
"CLIENT_RESTART",
|
||||
"N_PAUSE",
|
||||
"N_RECONNECT",
|
||||
"N_KEY_LIMIT_RENEG",
|
||||
"KEY_STATE_ERROR",
|
||||
"PROXY_ERROR",
|
||||
"PROXY_NEED_CREDS",
|
||||
"KEV_NEGOTIATE_ERROR",
|
||||
"KEV_EXPIRE_ERROR",
|
||||
"KEV_PENDING_ERROR",
|
||||
"N_KEV_EXPIRE",
|
||||
"PKTID_INVALID",
|
||||
"PKTID_BACKTRACK",
|
||||
"PKTID_EXPIRE",
|
||||
|
@ -38,10 +38,6 @@ namespace openvpn {
|
||||
public:
|
||||
typedef reliable::id_t id_t;
|
||||
|
||||
enum {
|
||||
RETRANSMIT = 2 // retransmit in N seconds if ACK not received
|
||||
};
|
||||
|
||||
class Message : public ReliableMessageBase<PACKET>
|
||||
{
|
||||
friend class ReliableSendTemplate;
|
||||
@ -61,9 +57,9 @@ namespace openvpn {
|
||||
return ret;
|
||||
}
|
||||
|
||||
void reset_retransmit(const Time& now)
|
||||
void reset_retransmit(const Time& now, const Time::Duration& tls_timeout)
|
||||
{
|
||||
retransmit_at_ = now + Time::Duration::seconds(RETRANSMIT);
|
||||
retransmit_at_ = now + tls_timeout;
|
||||
}
|
||||
|
||||
private:
|
||||
@ -128,11 +124,11 @@ namespace openvpn {
|
||||
// Return a fresh Message object that can be used to
|
||||
// construct the next packet in the sequence. Don't call
|
||||
// unless ready() returns true.
|
||||
Message& send(const Time& now)
|
||||
Message& send(const Time& now, const Time::Duration& tls_timeout)
|
||||
{
|
||||
Message& msg = window_.ref_by_id(next);
|
||||
msg.id_ = next++;
|
||||
msg.reset_retransmit(now);
|
||||
msg.reset_retransmit(now, tls_timeout);
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
@ -499,6 +499,16 @@ namespace openvpn {
|
||||
ManLink::send->float_notify(addr);
|
||||
}
|
||||
|
||||
virtual void data_limit_notify(const int key_id,
|
||||
const DataLimit::Mode cdl_mode,
|
||||
const DataLimit::State cdl_status)
|
||||
{
|
||||
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)
|
||||
|
188
openvpn/ssl/datalimit.hpp
Normal file
188
openvpn/ssl/datalimit.hpp
Normal file
@ -0,0 +1,188 @@
|
||||
// OpenVPN
|
||||
// Copyright (C) 2012-2015 OpenVPN Technologies, Inc.
|
||||
// All rights reserved
|
||||
|
||||
#ifndef OPENVPN_SSL_DATALIMIT_H
|
||||
#define OPENVPN_SSL_DATALIMIT_H
|
||||
|
||||
#include <openvpn/common/exception.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
// Helper for handling keys which can have an upper limit
|
||||
// on maximum amount of data encrypted/decrypted, such
|
||||
// as Blowfish.
|
||||
class DataLimit
|
||||
{
|
||||
public:
|
||||
typedef unsigned int size_type;
|
||||
|
||||
enum Mode {
|
||||
Encrypt=0,
|
||||
Decrypt=1,
|
||||
};
|
||||
|
||||
enum State {
|
||||
None=0,
|
||||
Green=1,
|
||||
Red=2,
|
||||
};
|
||||
|
||||
struct Parameters
|
||||
{
|
||||
size_type encrypt_red_limit = 0;
|
||||
size_type decrypt_red_limit = 0;
|
||||
};
|
||||
|
||||
DataLimit(const Parameters& p)
|
||||
: encrypt(p.encrypt_red_limit),
|
||||
decrypt(p.decrypt_red_limit)
|
||||
{
|
||||
}
|
||||
|
||||
State update_state(const Mode mode, const State newstate)
|
||||
{
|
||||
return elgible(mode, component(mode).update_state(newstate));
|
||||
}
|
||||
|
||||
State add(const Mode mode, const size_type n)
|
||||
{
|
||||
return elgible(mode, component(mode).add(n));
|
||||
}
|
||||
|
||||
bool is_decrypt_green()
|
||||
{
|
||||
return decrypt.get_state() >= Green;
|
||||
}
|
||||
|
||||
static const char *mode_str(const Mode m)
|
||||
{
|
||||
switch (m)
|
||||
{
|
||||
case Encrypt:
|
||||
return "Encrypt";
|
||||
case Decrypt:
|
||||
return "Decrypt";
|
||||
default:
|
||||
return "Mode_???";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *state_str(const State s)
|
||||
{
|
||||
switch (s)
|
||||
{
|
||||
case None:
|
||||
return "None";
|
||||
case Green:
|
||||
return "Green";
|
||||
case Red:
|
||||
return "Red";
|
||||
default:
|
||||
return "State_???";
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// Don't return Encrypt-Red until Decrypt-Green
|
||||
// has been received. This confirms that the peer
|
||||
// is now transmitting on the key ID, making it
|
||||
// eligible for renegotiation.
|
||||
State elgible(const Mode mode, const State state)
|
||||
{
|
||||
// Bit positions for Encrypt/Decrypt and Green/Red
|
||||
enum {
|
||||
EG = 1<<0,
|
||||
ER = 1<<1,
|
||||
DG = 1<<2,
|
||||
DR = 1<<3,
|
||||
};
|
||||
if (state > None)
|
||||
{
|
||||
const unsigned int mask = 1 << ((int(state) - 1) + (int(mode) << 1));
|
||||
if (!(flags & mask))
|
||||
{
|
||||
flags |= mask;
|
||||
if ((mask & (ER|DG)) && ((flags & (ER|DG)) == (ER|DG)))
|
||||
return Red;
|
||||
else if (mask & ER)
|
||||
return None;
|
||||
else
|
||||
return state;
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
class Component
|
||||
{
|
||||
public:
|
||||
Component(const size_type red_limit_arg)
|
||||
: red_limit(red_limit_arg)
|
||||
{
|
||||
}
|
||||
|
||||
State add(const size_type n)
|
||||
{
|
||||
bytes += n;
|
||||
return update_state(transition(state));
|
||||
}
|
||||
|
||||
State update_state(const State newstate)
|
||||
{
|
||||
State ret = None;
|
||||
if (newstate > state)
|
||||
state = ret = newstate;
|
||||
return ret;
|
||||
}
|
||||
|
||||
State get_state() const
|
||||
{
|
||||
return state;
|
||||
}
|
||||
|
||||
private:
|
||||
State transition(State s) const
|
||||
{
|
||||
switch (s)
|
||||
{
|
||||
case None:
|
||||
if (bytes)
|
||||
return Green;
|
||||
else
|
||||
return None;
|
||||
case Green:
|
||||
if (red_limit && bytes >= red_limit)
|
||||
return Red;
|
||||
else
|
||||
return None;
|
||||
case Red:
|
||||
default:
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
const size_type red_limit;
|
||||
size_type bytes = 0;
|
||||
State state = None;
|
||||
};
|
||||
|
||||
Component& component(const Mode m)
|
||||
{
|
||||
switch (m)
|
||||
{
|
||||
case Encrypt:
|
||||
return encrypt;
|
||||
case Decrypt:
|
||||
return decrypt;
|
||||
default:
|
||||
throw Exception("DataLimit::Component: unknown mode");
|
||||
}
|
||||
}
|
||||
|
||||
Component encrypt;
|
||||
Component decrypt;
|
||||
unsigned int flags = 0;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -44,6 +44,7 @@
|
||||
#include <openvpn/common/number.hpp>
|
||||
#include <openvpn/common/likely.hpp>
|
||||
#include <openvpn/common/string.hpp>
|
||||
#include <openvpn/common/format.hpp>
|
||||
#include <openvpn/buffer/buffer.hpp>
|
||||
#include <openvpn/buffer/safestr.hpp>
|
||||
#include <openvpn/buffer/bufcomposed.hpp>
|
||||
@ -57,10 +58,12 @@
|
||||
#include <openvpn/crypto/ovpnhmac.hpp>
|
||||
#include <openvpn/crypto/packet_id.hpp>
|
||||
#include <openvpn/crypto/static_key.hpp>
|
||||
#include <openvpn/crypto/bs64_data_limit.hpp>
|
||||
#include <openvpn/log/sessionstats.hpp>
|
||||
#include <openvpn/ssl/protostack.hpp>
|
||||
#include <openvpn/ssl/psid.hpp>
|
||||
#include <openvpn/ssl/tlsprf.hpp>
|
||||
#include <openvpn/ssl/datalimit.hpp>
|
||||
#include <openvpn/transport/protocol.hpp>
|
||||
#include <openvpn/tun/layer.hpp>
|
||||
#include <openvpn/tun/tunmtu.hpp>
|
||||
@ -300,6 +303,7 @@ namespace openvpn {
|
||||
Time::Duration become_primary; // KeyContext (that is ACTIVE) becomes primary at this time
|
||||
Time::Duration renegotiate; // start SSL/TLS renegotiation at this time
|
||||
Time::Duration expire; // KeyContext expires at this time
|
||||
Time::Duration tls_timeout; // Packet retransmit timeout on TLS control channel
|
||||
|
||||
// keepalive parameters
|
||||
Time::Duration keepalive_ping;
|
||||
@ -333,6 +337,7 @@ namespace openvpn {
|
||||
max_ack_list = 4;
|
||||
handshake_window = Time::Duration::seconds(60);
|
||||
renegotiate = Time::Duration::seconds(3600);
|
||||
tls_timeout = Time::Duration::seconds(1);
|
||||
keepalive_ping = Time::Duration::seconds(8);
|
||||
keepalive_timeout = Time::Duration::seconds(40);
|
||||
comp_ctx = CompressContext(CompressContext::NONE, false);
|
||||
@ -340,9 +345,6 @@ namespace openvpn {
|
||||
pid_mode = PacketIDReceive::UDP_MODE;
|
||||
key_direction = default_key_direction;
|
||||
|
||||
// load parameters that can be present in both config file or pushed options
|
||||
load_common(opt, pco, server ? LOAD_COMMON_SERVER : LOAD_COMMON_CLIENT);
|
||||
|
||||
// layer
|
||||
{
|
||||
const Option* dev = opt.get_ptr("dev-type");
|
||||
@ -467,20 +469,14 @@ namespace openvpn {
|
||||
|
||||
// tun-mtu
|
||||
tun_mtu = parse_tun_mtu(opt, tun_mtu);
|
||||
|
||||
// load parameters that can be present in both config file or pushed options
|
||||
load_common(opt, pco, server ? LOAD_COMMON_SERVER : LOAD_COMMON_CLIENT);
|
||||
}
|
||||
|
||||
// load options string pushed by server
|
||||
void process_push(const OptionList& opt, const ProtoContextOptions& pco)
|
||||
{
|
||||
try {
|
||||
// load parameters that can be present in both config file or pushed options
|
||||
load_common(opt, pco, LOAD_COMMON_CLIENT_PUSHED);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
OPENVPN_THROW(process_server_push_error, "Problem accepting server-pushed parameter: " << e.what());
|
||||
}
|
||||
|
||||
// data channel
|
||||
{
|
||||
// cipher
|
||||
@ -569,6 +565,15 @@ namespace openvpn {
|
||||
OPENVPN_THROW(process_server_push_error, "Problem accepting server-pushed peer-id: " << e.what());
|
||||
}
|
||||
|
||||
try {
|
||||
// load parameters that can be present in both config file or pushed options
|
||||
load_common(opt, pco, LOAD_COMMON_CLIENT_PUSHED);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
OPENVPN_THROW(process_server_push_error, "Problem accepting server-pushed parameter: " << e.what());
|
||||
}
|
||||
|
||||
// show negotiated options
|
||||
OPENVPN_LOG_STRING_PROTO(show_options());
|
||||
}
|
||||
@ -687,6 +692,8 @@ namespace openvpn {
|
||||
out << compstr;
|
||||
if (extra_peer_info)
|
||||
out << extra_peer_info->to_string();
|
||||
if (is_bs64_cipher(dc.cipher()))
|
||||
out << "IV_BS64DL=1\n"; // indicate support for data limits when using 64-bit block-size ciphers, version 1 (CVE-2016-6329)
|
||||
const std::string ret = out.str();
|
||||
OPENVPN_LOG_PROTO("Peer Info:" << std::endl << ret);
|
||||
return ret;
|
||||
@ -716,13 +723,22 @@ namespace openvpn {
|
||||
const LoadCommonType type)
|
||||
{
|
||||
// duration parms
|
||||
load_duration_parm(renegotiate, "reneg-sec", opt, 10, false);
|
||||
load_duration_parm(renegotiate, "reneg-sec", opt, 10, false, false);
|
||||
expire = renegotiate;
|
||||
load_duration_parm(expire, "tran-window", opt, 10, false);
|
||||
load_duration_parm(expire, "tran-window", opt, 10, false, false);
|
||||
expire += renegotiate;
|
||||
load_duration_parm(handshake_window, "hand-window", opt, 10, false);
|
||||
become_primary = Time::Duration::seconds(std::min(handshake_window.to_seconds(),
|
||||
load_duration_parm(handshake_window, "hand-window", opt, 10, false, false);
|
||||
if (is_bs64_cipher(dc.cipher())) // special data limits for 64-bit block-size ciphers (CVE-2016-6329)
|
||||
{
|
||||
become_primary = Time::Duration::seconds(5);
|
||||
tls_timeout = Time::Duration::milliseconds(1000);
|
||||
}
|
||||
else
|
||||
become_primary = Time::Duration::seconds(std::min(handshake_window.to_seconds(),
|
||||
renegotiate.to_seconds() / 2));
|
||||
load_duration_parm(become_primary, "become-primary", opt, 0, false, false);
|
||||
load_duration_parm(tls_timeout, "tls-timeout", opt, 100, false, true);
|
||||
|
||||
if (type == LOAD_COMMON_SERVER)
|
||||
renegotiate += handshake_window; // avoid renegotiation collision with client
|
||||
|
||||
@ -731,13 +747,13 @@ namespace openvpn {
|
||||
const Option *o = opt.get_ptr("keepalive");
|
||||
if (o)
|
||||
{
|
||||
set_duration_parm(keepalive_ping, "keepalive ping", o->get(1, 16), 1, false);
|
||||
set_duration_parm(keepalive_timeout, "keepalive timeout", o->get(2, 16), 1, type == LOAD_COMMON_SERVER);
|
||||
set_duration_parm(keepalive_ping, "keepalive ping", o->get(1, 16), 1, false, false);
|
||||
set_duration_parm(keepalive_timeout, "keepalive timeout", o->get(2, 16), 1, type == LOAD_COMMON_SERVER, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
load_duration_parm(keepalive_ping, "ping", opt, 1, false);
|
||||
load_duration_parm(keepalive_timeout, "ping-restart", opt, 1, false);
|
||||
load_duration_parm(keepalive_ping, "ping", opt, 1, false, false);
|
||||
load_duration_parm(keepalive_timeout, "ping-restart", opt, 1, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -834,8 +850,6 @@ namespace openvpn {
|
||||
}
|
||||
}
|
||||
|
||||
bool is_secondary() const { return flags & SECONDARY; }
|
||||
|
||||
unsigned int flags;
|
||||
unsigned int opcode;
|
||||
int peer_id_;
|
||||
@ -1112,25 +1126,101 @@ namespace openvpn {
|
||||
public:
|
||||
typedef RCPtr<KeyContext> Ptr;
|
||||
|
||||
// timeline of events for KeyContext (occurring in order)
|
||||
// KeyContext events occur on two basic key types:
|
||||
// Primary Key -- the key we transmit/encrypt on.
|
||||
// Secondary Key -- new keys and retiring keys.
|
||||
//
|
||||
// The very first key created (key_id == 0) is a
|
||||
// primary key. Subsequently created keys are always,
|
||||
// at least initially, secondary keys. Secondary keys
|
||||
// promote to primary via the KEV_BECOME_PRIMARY event
|
||||
// (actually KEV_BECOME_PRIMARY swaps the primary and
|
||||
// secondary keys, so the old primary is demoted
|
||||
// to secondary and marked for expiration).
|
||||
//
|
||||
// Secondary keys are created by:
|
||||
// 1. locally-generated soft renegotiation requests, and
|
||||
// 2. peer-requested soft renegotiation requests.
|
||||
// In each case, any previous secondary key will be
|
||||
// wiped (including a secondary key that exists due to
|
||||
// demotion of a previous primary key that has been marked
|
||||
// for expiration).
|
||||
enum EventType {
|
||||
KEV_NONE,
|
||||
KEV_ACTIVE, // KeyContext has reached the ACTIVE state
|
||||
KEV_NEGOTIATE, // SSL/TLS negotiation must complete by this time
|
||||
KEV_BECOME_PRIMARY, // KeyContext becomes primary for data channel traffic
|
||||
KEV_RENEGOTIATE, // start renegotiating a new KeyContext at this time
|
||||
KEV_EXPIRE, // expiration of KeyContext
|
||||
KEV_NEGOTIATE_FAILED, // SSL/TLS negotiation failed
|
||||
|
||||
// KeyContext has reached the ACTIVE state, occurs on both
|
||||
// primary and secondary.
|
||||
KEV_ACTIVE,
|
||||
|
||||
// SSL/TLS negotiation must complete by this time. If this
|
||||
// event is hit on the first primary (i.e. first KeyContext
|
||||
// with key_id == 0), it is fatal to the session and will
|
||||
// trigger a disconnect/reconnect. If it's hit on the
|
||||
// secondary, it will trigger a soft renegotiation.
|
||||
KEV_NEGOTIATE,
|
||||
|
||||
// When a KeyContext (normally the secondary) is scheduled
|
||||
// to transition to the primary state.
|
||||
KEV_BECOME_PRIMARY,
|
||||
|
||||
// Waiting for condition on secondary (usually
|
||||
// dataflow-based) to trigger KEV_BECOME_PRIMARY.
|
||||
KEV_PRIMARY_PENDING,
|
||||
|
||||
// Start renegotiating a new KeyContext on secondary
|
||||
// (ignored unless originating on primary).
|
||||
KEV_RENEGOTIATE,
|
||||
|
||||
// Trigger a renegotiation originating from either
|
||||
// primary or secondary.
|
||||
KEV_RENEGOTIATE_FORCE,
|
||||
|
||||
// Queue delayed renegotiation request from secondary
|
||||
// to take effect after KEV_BECOME_PRIMARY.
|
||||
KEV_RENEGOTIATE_QUEUE,
|
||||
|
||||
// Expiration of KeyContext.
|
||||
KEV_EXPIRE,
|
||||
};
|
||||
|
||||
// for debugging
|
||||
static const char *event_type_string(const EventType et)
|
||||
{
|
||||
switch (et)
|
||||
{
|
||||
case KEV_NONE:
|
||||
return "KEV_NONE";
|
||||
case KEV_ACTIVE:
|
||||
return "KEV_ACTIVE";
|
||||
case KEV_NEGOTIATE:
|
||||
return "KEV_NEGOTIATE";
|
||||
case KEV_BECOME_PRIMARY:
|
||||
return "KEV_BECOME_PRIMARY";
|
||||
case KEV_PRIMARY_PENDING:
|
||||
return "KEV_PRIMARY_PENDING";
|
||||
case KEV_RENEGOTIATE:
|
||||
return "KEV_RENEGOTIATE";
|
||||
case KEV_RENEGOTIATE_FORCE:
|
||||
return "KEV_RENEGOTIATE_FORCE";
|
||||
case KEV_RENEGOTIATE_QUEUE:
|
||||
return "KEV_RENEGOTIATE_QUEUE";
|
||||
case KEV_EXPIRE:
|
||||
return "KEV_EXPIRE";
|
||||
default:
|
||||
return "KEV_?";
|
||||
}
|
||||
}
|
||||
|
||||
KeyContext(ProtoContext& p, const bool initiator)
|
||||
: Base(*p.config->ssl_factory, p.config->now, p.config->frame, p.stats,
|
||||
: Base(*p.config->ssl_factory,
|
||||
p.config->now, p.config->tls_timeout,
|
||||
p.config->frame, p.stats,
|
||||
p.config->reliable_window, p.config->max_ack_list),
|
||||
proto(p),
|
||||
state(STATE_UNDEF),
|
||||
crypto_flags(0),
|
||||
dirty(0),
|
||||
handled_pid_wrap(false),
|
||||
key_limit_renegotiation_fired(false),
|
||||
is_reliable(p.config->protocol.is_reliable()),
|
||||
tlsprf(p.config->tlsprf_factory->new_obj(p.is_server()))
|
||||
{
|
||||
@ -1232,8 +1322,12 @@ namespace openvpn {
|
||||
// compress and encrypt packet and prepend op header
|
||||
const bool pid_wrap = do_encrypt(buf, true);
|
||||
|
||||
// check for rare situation where packet ID is near overflow
|
||||
test_pid_wrap(pid_wrap);
|
||||
// Trigger a new SSL/TLS negotiation if packet ID (a 32-bit unsigned int)
|
||||
// is getting close to wrapping around. If it wraps back to 0 without
|
||||
// a renegotiation, it would cause the relay protection logic to wrongly
|
||||
// think that all further packets are replays.
|
||||
if (pid_wrap)
|
||||
schedule_key_limit_renegotiation();
|
||||
}
|
||||
else
|
||||
buf.reset_size(); // no crypto context available
|
||||
@ -1262,6 +1356,10 @@ namespace openvpn {
|
||||
invalidate(err);
|
||||
}
|
||||
|
||||
// trigger renegotiation if we hit decrypt data limit
|
||||
if (data_limit)
|
||||
data_limit_add(DataLimit::Decrypt, buf.size());
|
||||
|
||||
// decompress packet
|
||||
if (compress)
|
||||
compress->decompress(buf);
|
||||
@ -1280,9 +1378,34 @@ namespace openvpn {
|
||||
|
||||
// usually called by parent ProtoContext object when this KeyContext
|
||||
// has been retired.
|
||||
void prepare_expire()
|
||||
void prepare_expire(const EventType current_ev = KeyContext::KEV_NONE)
|
||||
{
|
||||
set_event(KEV_NONE, KEV_EXPIRE, construct_time + proto.config->expire);
|
||||
set_event(current_ev,
|
||||
KEV_EXPIRE,
|
||||
key_limit_renegotiation_fired ? data_limit_expire() : construct_time + proto.config->expire);
|
||||
}
|
||||
|
||||
// set a default next event, if unspecified
|
||||
void set_next_event_if_unspecified()
|
||||
{
|
||||
if (next_event == KEV_NONE && !invalidated())
|
||||
prepare_expire();
|
||||
}
|
||||
|
||||
// set a key limit renegotiation event at time t
|
||||
void key_limit_reneg(const EventType ev, const Time& t)
|
||||
{
|
||||
if (t.defined())
|
||||
set_event(KEV_NONE, ev, t + Time::Duration::seconds(proto.is_server() ? 2 : 1));
|
||||
}
|
||||
|
||||
// return time of upcoming KEV_BECOME_PRIMARY event
|
||||
Time become_primary_time()
|
||||
{
|
||||
if (next_event == KEV_BECOME_PRIMARY)
|
||||
return next_event_time;
|
||||
else
|
||||
return Time();
|
||||
}
|
||||
|
||||
// is an KEV_x event pending?
|
||||
@ -1297,7 +1420,7 @@ namespace openvpn {
|
||||
EventType get_event() const { return current_event; }
|
||||
|
||||
// clear KEV_x event
|
||||
void reset_event() { set_event(KEV_NONE); }
|
||||
void reset_event() { current_event = KEV_NONE; }
|
||||
|
||||
// was session invalidated by an exception?
|
||||
bool invalidated() const { return Base::invalidated(); }
|
||||
@ -1455,6 +1578,16 @@ namespace openvpn {
|
||||
const unsigned int key_dir = proto.is_server() ? OpenVPNStaticKey::INVERSE : OpenVPNStaticKey::NORMAL;
|
||||
const OpenVPNStaticKey& key = data_channel_key->key;
|
||||
|
||||
// special data limits for 64-bit block-size ciphers (CVE-2016-6329)
|
||||
if (is_bs64_cipher(c.dc.cipher()))
|
||||
{
|
||||
DataLimit::Parameters dp;
|
||||
dp.encrypt_red_limit = OPENVPN_BS64_DATA_LIMIT;
|
||||
dp.decrypt_red_limit = OPENVPN_BS64_DATA_LIMIT;
|
||||
OPENVPN_LOG_PROTO("Per-Key Data Limit: " << dp.encrypt_red_limit << '/' << dp.decrypt_red_limit);
|
||||
data_limit.reset(new DataLimit(dp));
|
||||
}
|
||||
|
||||
// build crypto context for data channel encryption/decryption
|
||||
crypto = c.dc.context().new_obj(key_id_);
|
||||
crypto_flags = crypto->defined();
|
||||
@ -1490,6 +1623,13 @@ namespace openvpn {
|
||||
}
|
||||
}
|
||||
|
||||
void data_limit_notify(const DataLimit::Mode cdl_mode,
|
||||
const DataLimit::State cdl_status)
|
||||
{
|
||||
if (data_limit)
|
||||
data_limit_event(cdl_mode, data_limit->update_state(cdl_mode, cdl_status));
|
||||
}
|
||||
|
||||
private:
|
||||
bool do_encrypt(BufferAllocated& buf, const bool compress_hint)
|
||||
{
|
||||
@ -1499,6 +1639,10 @@ namespace openvpn {
|
||||
if (compress)
|
||||
compress->compress(buf, compress_hint);
|
||||
|
||||
// trigger renegotiation if we hit encrypt data limit
|
||||
if (data_limit)
|
||||
data_limit_add(DataLimit::Encrypt, buf.size());
|
||||
|
||||
if (enable_op32)
|
||||
{
|
||||
const std::uint32_t op32 = htonl(op32_compose(DATA_V2, key_id_, remote_peer_id));
|
||||
@ -1531,26 +1675,19 @@ namespace openvpn {
|
||||
|
||||
void set_state(const int newstate)
|
||||
{
|
||||
OPENVPN_LOG_PROTO_VERBOSE("KeyContext[" << key_id_ << "] " << (proto.is_server() ? "SERVER " : "CLIENT ") << state_string(state) << " -> " << state_string(newstate));
|
||||
OPENVPN_LOG_PROTO_VERBOSE(proto.debug_prefix() << " KeyContext[" << key_id_ << "] " << state_string(state) << " -> " << state_string(newstate));
|
||||
state = newstate;
|
||||
}
|
||||
|
||||
void set_event(const EventType current)
|
||||
{
|
||||
OPENVPN_LOG_PROTO_VERBOSE("KeyContext[" << key_id_ << "] " << event_type_string(current));
|
||||
OPENVPN_LOG_PROTO_VERBOSE(proto.debug_prefix() << " KeyContext[" << key_id_ << "] " << event_type_string(current));
|
||||
current_event = current;
|
||||
}
|
||||
|
||||
void set_event(const EventType next, const Time& next_time)
|
||||
{
|
||||
OPENVPN_LOG_PROTO_VERBOSE("KeyContext[" << key_id_ << "] " << event_type_string(next) << '(' << seconds_until(next_time) << ')');
|
||||
next_event = next;
|
||||
next_event_time = next_time;
|
||||
}
|
||||
|
||||
void set_event(const EventType current, const EventType next, const Time& next_time)
|
||||
{
|
||||
OPENVPN_LOG_PROTO_VERBOSE("KeyContext[" << key_id_ << "] " << event_type_string(current) << " -> " << event_type_string(next) << '(' << seconds_until(next_time) << ')');
|
||||
OPENVPN_LOG_PROTO_VERBOSE(proto.debug_prefix() << " KeyContext[" << key_id_ << "] " << event_type_string(current) << " -> " << event_type_string(next) << '(' << seconds_until(next_time) << ')');
|
||||
current_event = current;
|
||||
next_event = next;
|
||||
next_event_time = next_time;
|
||||
@ -1559,31 +1696,76 @@ namespace openvpn {
|
||||
void invalidate_callback() // called by ProtoStackBase when session is invalidated
|
||||
{
|
||||
reached_active_time_ = Time();
|
||||
set_event(KEV_NONE, Time::infinite());
|
||||
next_event = KEV_NONE;
|
||||
next_event_time = Time::infinite();
|
||||
}
|
||||
|
||||
// Trigger a new SSL/TLS negotiation if packet ID (a 32-bit unsigned int)
|
||||
// is getting close to wrapping around. If it wraps back to 0 without
|
||||
// a renegotiation, it would cause the relay protection logic to wrongly
|
||||
// think that all further packets are replays.
|
||||
void test_pid_wrap(const bool pid_wrap)
|
||||
// Trigger a renegotiation based on data flow condition such
|
||||
// as per-key data limit or packet ID approaching wraparound.
|
||||
void schedule_key_limit_renegotiation()
|
||||
{
|
||||
if (pid_wrap && !handled_pid_wrap)
|
||||
if (!key_limit_renegotiation_fired && state >= ACTIVE && !invalidated())
|
||||
{
|
||||
trigger_renegotiation();
|
||||
handled_pid_wrap = true;
|
||||
OPENVPN_LOG_PROTO_VERBOSE(proto.debug_prefix() << " SCHEDULE KEY LIMIT RENEGOTIATION");
|
||||
|
||||
key_limit_renegotiation_fired = true;
|
||||
proto.stats->error(Error::N_KEY_LIMIT_RENEG);
|
||||
|
||||
// If primary, renegotiate now (within a second or two).
|
||||
// If secondary, queue the renegotiation request until
|
||||
// key reaches primary.
|
||||
if (next_event == KEV_BECOME_PRIMARY) // secondary key before transition to primary?
|
||||
set_event(KEV_RENEGOTIATE_QUEUE); // reneg request crosses over to primary, doesn't wipe next_event (KEV_BECOME_PRIMARY)
|
||||
else
|
||||
key_limit_reneg(KEV_RENEGOTIATE, *now);
|
||||
}
|
||||
}
|
||||
|
||||
void trigger_renegotiation()
|
||||
// Handle data-limited keys such as Blowfish and other 64-bit block-size ciphers.
|
||||
void data_limit_add(const DataLimit::Mode mode, const size_t size)
|
||||
{
|
||||
if (state >= ACTIVE && !invalidated())
|
||||
set_event(KEV_RENEGOTIATE, KEV_EXPIRE, construct_time + proto.config->expire);
|
||||
const DataLimit::State state = data_limit->add(mode, size);
|
||||
if (state > DataLimit::None)
|
||||
data_limit_event(mode, state);
|
||||
}
|
||||
|
||||
// Handle a DataLimit event.
|
||||
void data_limit_event(const DataLimit::Mode mode, const DataLimit::State state)
|
||||
{
|
||||
OPENVPN_LOG_PROTO_VERBOSE(proto.debug_prefix() << " DATA LIMIT " << DataLimit::mode_str(mode) << ' ' << DataLimit::state_str(state) << " key_id=" << key_id_);
|
||||
|
||||
// State values:
|
||||
// DataLimit::Green -- first packet received and decrypted.
|
||||
// DataLimit::Red -- data limit has been exceeded, so trigger a renegotiation.
|
||||
if (state == DataLimit::Red)
|
||||
schedule_key_limit_renegotiation();
|
||||
|
||||
// When we are in KEV_PRIMARY_PENDING state, we must receive at least
|
||||
// one packet from the peer on this key before we transition to
|
||||
// KEV_BECOME_PRIMARY so we can transmit on it.
|
||||
if (next_event == KEV_PRIMARY_PENDING && data_limit->is_decrypt_green())
|
||||
set_event(KEV_NONE, KEV_BECOME_PRIMARY, *now + Time::Duration::seconds(1));
|
||||
}
|
||||
|
||||
// Should we enter KEV_PRIMARY_PENDING state? Do it if:
|
||||
// 1. we are a client,
|
||||
// 2. data limit is enabled,
|
||||
// 3. this is a renegotiated key in secondary context, i.e. not the first key, and
|
||||
// 4. no data received yet from peer on this key.
|
||||
bool data_limit_defer() const
|
||||
{
|
||||
return !proto.is_server() && data_limit && key_id_ && !data_limit->is_decrypt_green();
|
||||
}
|
||||
|
||||
// General expiration set when key hits data limit threshold.
|
||||
Time data_limit_expire() const
|
||||
{
|
||||
return *now + (proto.config->handshake_window * 2);
|
||||
}
|
||||
|
||||
void active_event()
|
||||
{
|
||||
set_event(KEV_ACTIVE, KEV_BECOME_PRIMARY, construct_time + proto.config->become_primary);
|
||||
set_event(KEV_ACTIVE, KEV_BECOME_PRIMARY, reached_active() + proto.config->become_primary);
|
||||
}
|
||||
|
||||
void process_next_event()
|
||||
@ -1592,26 +1774,24 @@ namespace openvpn {
|
||||
{
|
||||
switch (next_event)
|
||||
{
|
||||
case KEV_NEGOTIATE:
|
||||
if (state >= ACTIVE)
|
||||
set_event(KEV_NEGOTIATE, KEV_BECOME_PRIMARY, construct_time + proto.config->become_primary);
|
||||
else
|
||||
{
|
||||
proto.stats->error(Error::KEV_NEGOTIATE_ERROR);
|
||||
invalidate(Error::KEV_NEGOTIATE_ERROR);
|
||||
set_event(KEV_NEGOTIATE_FAILED);
|
||||
}
|
||||
break;
|
||||
case KEV_BECOME_PRIMARY:
|
||||
set_event(KEV_BECOME_PRIMARY, KEV_RENEGOTIATE, construct_time + proto.config->renegotiate);
|
||||
if (data_limit_defer())
|
||||
set_event(KEV_NONE, KEV_PRIMARY_PENDING, data_limit_expire());
|
||||
else
|
||||
set_event(KEV_BECOME_PRIMARY, KEV_RENEGOTIATE, construct_time + proto.config->renegotiate);
|
||||
break;
|
||||
case KEV_RENEGOTIATE:
|
||||
set_event(KEV_RENEGOTIATE, KEV_EXPIRE, construct_time + proto.config->expire);
|
||||
case KEV_RENEGOTIATE_FORCE:
|
||||
prepare_expire(next_event);
|
||||
break;
|
||||
case KEV_NEGOTIATE:
|
||||
kev_error(KEV_NEGOTIATE, Error::KEV_NEGOTIATE_ERROR);
|
||||
break;
|
||||
case KEV_PRIMARY_PENDING:
|
||||
kev_error(KEV_PRIMARY_PENDING, Error::KEV_PENDING_ERROR);
|
||||
break;
|
||||
case KEV_EXPIRE:
|
||||
proto.stats->error(Error::KEV_EXPIRE_ERROR);
|
||||
invalidate(Error::KEV_EXPIRE_ERROR);
|
||||
set_event(KEV_EXPIRE);
|
||||
kev_error(KEV_EXPIRE, Error::N_KEV_EXPIRE);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -1619,6 +1799,13 @@ namespace openvpn {
|
||||
}
|
||||
}
|
||||
|
||||
void kev_error(const EventType ev, const Error::Type reason)
|
||||
{
|
||||
proto.stats->error(reason);
|
||||
invalidate(reason);
|
||||
set_event(ev);
|
||||
}
|
||||
|
||||
unsigned int initial_op(const bool sender) const
|
||||
{
|
||||
if (key_id_)
|
||||
@ -1803,7 +1990,7 @@ namespace openvpn {
|
||||
{
|
||||
std::unique_ptr<DataChannelKey> dck(new DataChannelKey());
|
||||
tlsprf->generate_key_expansion(dck->key, proto.psid_self, proto.psid_peer);
|
||||
OPENVPN_LOG_PROTO_VERBOSE("KEY " << proto.mode().str() << ' ' << dck->key.render());
|
||||
OPENVPN_LOG_PROTO_VERBOSE(proto.debug_prefix() << " KEY " << proto.mode().str() << ' ' << dck->key.render());
|
||||
tlsprf->erase();
|
||||
dck.swap(data_channel_key);
|
||||
if (!proto.dc_deferred)
|
||||
@ -2060,30 +2247,6 @@ namespace openvpn {
|
||||
gen_head(ACK_V1, buf);
|
||||
}
|
||||
|
||||
// for debugging
|
||||
static const char *event_type_string(const EventType et)
|
||||
{
|
||||
switch (et)
|
||||
{
|
||||
case KEV_NONE:
|
||||
return "KEV_NONE";
|
||||
case KEV_ACTIVE:
|
||||
return "KEV_ACTIVE";
|
||||
case KEV_NEGOTIATE:
|
||||
return "KEV_NEGOTIATE";
|
||||
case KEV_BECOME_PRIMARY:
|
||||
return "KEV_BECOME_PRIMARY";
|
||||
case KEV_RENEGOTIATE:
|
||||
return "KEV_RENEGOTIATE";
|
||||
case KEV_EXPIRE:
|
||||
return "KEV_EXPIRE";
|
||||
case KEV_NEGOTIATE_FAILED:
|
||||
return "KEV_NEGOTIATE_FAILED";
|
||||
default:
|
||||
return "KEV_?";
|
||||
}
|
||||
}
|
||||
|
||||
// for debugging
|
||||
static const char *state_string(const int s)
|
||||
{
|
||||
@ -2135,7 +2298,7 @@ namespace openvpn {
|
||||
int remote_peer_id; // -1 to disable
|
||||
bool enable_op32;
|
||||
bool dirty;
|
||||
bool handled_pid_wrap;
|
||||
bool key_limit_renegotiation_fired;
|
||||
bool is_reliable;
|
||||
Compress::Ptr compress;
|
||||
CryptoDCInstance::Ptr crypto;
|
||||
@ -2148,6 +2311,7 @@ namespace openvpn {
|
||||
std::deque<BufferPtr> app_pre_write_queue;
|
||||
std::unique_ptr<DataChannelKey> data_channel_key;
|
||||
BufferComposed app_recv_buf;
|
||||
std::unique_ptr<DataLimit> data_limit;
|
||||
};
|
||||
|
||||
public:
|
||||
@ -2244,9 +2408,6 @@ namespace openvpn {
|
||||
// validate options
|
||||
c.validate_complete();
|
||||
|
||||
// by default, fast_transition is turned off
|
||||
fast_transition = false;
|
||||
|
||||
// defer data channel initialization until after client options pull?
|
||||
dc_deferred = c.dc_deferred;
|
||||
|
||||
@ -2292,7 +2453,7 @@ namespace openvpn {
|
||||
|
||||
// initialize key contexts
|
||||
primary.reset(new KeyContext(*this, is_client()));
|
||||
OPENVPN_LOG_PROTO_VERBOSE("New KeyContext PRIMARY id=" << primary->key_id());
|
||||
OPENVPN_LOG_PROTO_VERBOSE(debug_prefix() << " New KeyContext PRIMARY id=" << primary->key_id());
|
||||
|
||||
// initialize keepalive timers
|
||||
keepalive_expire = Time::infinite(); // initially disabled
|
||||
@ -2325,8 +2486,7 @@ namespace openvpn {
|
||||
void renegotiate()
|
||||
{
|
||||
// initialize secondary key context
|
||||
secondary.reset(new KeyContext(*this, true));
|
||||
OPENVPN_LOG_PROTO_VERBOSE("New KeyContext SECONDARY id=" << secondary->key_id() << " local-triggered");
|
||||
new_secondary_key(true);
|
||||
secondary->start();
|
||||
}
|
||||
|
||||
@ -2424,6 +2584,7 @@ namespace openvpn {
|
||||
// encrypt a data channel packet using primary KeyContext
|
||||
void data_encrypt(BufferAllocated& in_out)
|
||||
{
|
||||
//OPENVPN_LOG_PROTO_VERBOSE(debug_prefix() << " DATA ENCRYPT size=" << in_out.size());
|
||||
primary->encrypt(in_out);
|
||||
}
|
||||
|
||||
@ -2433,6 +2594,8 @@ namespace openvpn {
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
//OPENVPN_LOG_PROTO_VERBOSE(debug_prefix() << " DATA DECRYPT key_id=" << select_key_context(type, false).key_id() << " size=" << in_out.size());
|
||||
|
||||
select_key_context(type, false).decrypt(in_out);
|
||||
|
||||
// update time of most recent packet received
|
||||
@ -2488,21 +2651,6 @@ namespace openvpn {
|
||||
// reason for invalidation if invalidated() above returns true
|
||||
Error::Type invalidation_reason() const { return primary->invalidation_reason(); }
|
||||
|
||||
// Enable original OpenVPN behavior of switching to new SSL/TLS key
|
||||
// context for control channel sends immediately after renegotiation
|
||||
// reaches ACTIVE state. By default, we will transition to new
|
||||
// key context over the "become_primary" duration in Config.
|
||||
// The default behavior is known to be more reliable, but the
|
||||
// original behavior may be enabled by calling this method.
|
||||
void enable_fast_transition() { fast_transition = true; }
|
||||
|
||||
// A placeholder for enabling strict OpenVPN 2.x protocol
|
||||
// compatibility.
|
||||
void enable_strict_openvpn_2x()
|
||||
{
|
||||
enable_fast_transition();
|
||||
}
|
||||
|
||||
// Do late initialization of data channel, for example
|
||||
// on client after server push, or on server after client
|
||||
// capabilities are known.
|
||||
@ -2551,6 +2699,17 @@ namespace openvpn {
|
||||
keepalive_parms_modified();
|
||||
}
|
||||
|
||||
// Notify our component KeyContext when per-key Data Limits have been reached
|
||||
void data_limit_notify(const int key_id,
|
||||
const DataLimit::Mode cdl_mode,
|
||||
const DataLimit::State cdl_status)
|
||||
{
|
||||
if (key_id == primary->key_id())
|
||||
primary->data_limit_notify(cdl_mode, cdl_status);
|
||||
else if (secondary && key_id == secondary->key_id())
|
||||
secondary->data_limit_notify(cdl_mode, cdl_status);
|
||||
}
|
||||
|
||||
// access the data channel settings
|
||||
CryptoDCSettings& dc_settings()
|
||||
{
|
||||
@ -2651,8 +2810,7 @@ namespace openvpn {
|
||||
{
|
||||
if (KeyContext::validate(pkt.buffer(), *this, now_))
|
||||
{
|
||||
secondary.reset(new KeyContext(*this, false));
|
||||
OPENVPN_LOG_PROTO_VERBOSE("New KeyContext SECONDARY id=" << secondary->key_id() << " remote-triggered");
|
||||
new_secondary_key(false);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
@ -2681,26 +2839,15 @@ namespace openvpn {
|
||||
}
|
||||
|
||||
// Select a KeyContext (primary or secondary) for control channel sends.
|
||||
// NOTE: possible incompatibility with existing OpenVPN protocol.
|
||||
// Even after new key context goes active, we still wait for
|
||||
// KEV_BECOME_PRIMARY event before we use it for app-level control-channel
|
||||
// transmissions. Simulations have found this method to be more reliable.
|
||||
// To revert to old OpenVPN behavior, call enable_fast_transition() method.
|
||||
// KEV_BECOME_PRIMARY event (controlled by the become_primary duration
|
||||
// in Config) before we use it for app-level control-channel
|
||||
// transmissions. Simulations have found this method to be more reliable
|
||||
// than the immediate rollover practiced by OpenVPN 2.x.
|
||||
KeyContext& select_control_send_context()
|
||||
{
|
||||
if (!fast_transition || !secondary)
|
||||
{
|
||||
return *primary;
|
||||
}
|
||||
else
|
||||
{
|
||||
const Time p = primary->reached_active();
|
||||
const Time s = secondary->reached_active();
|
||||
if (p.defined() && s.defined() && s > p)
|
||||
return *secondary;
|
||||
else
|
||||
return *primary;
|
||||
}
|
||||
OPENVPN_LOG_PROTO_VERBOSE(debug_prefix() << " CONTROL SEND");
|
||||
return *primary;
|
||||
}
|
||||
|
||||
// Possibly send a keepalive message, and check for expiration
|
||||
@ -2746,6 +2893,17 @@ namespace openvpn {
|
||||
return did_work;
|
||||
}
|
||||
|
||||
// Create a new secondary key.
|
||||
// initiator --
|
||||
// false : remote renegotiation request
|
||||
// true : local renegotiation request
|
||||
void new_secondary_key(const bool initiator)
|
||||
{
|
||||
// Create the secondary
|
||||
secondary.reset(new KeyContext(*this, initiator));
|
||||
OPENVPN_LOG_PROTO_VERBOSE(debug_prefix() << " New KeyContext SECONDARY id=" << secondary->key_id() << (initiator ? " local-triggered" : " remote-triggered"));
|
||||
}
|
||||
|
||||
// Promote a newly renegotiated KeyContext to primary status.
|
||||
// This is usually triggered by become_primary variable (Time::Duration)
|
||||
// in Config.
|
||||
@ -2754,7 +2912,7 @@ namespace openvpn {
|
||||
primary.swap(secondary);
|
||||
primary->rekey(CryptoDCInstance::PROMOTE_SECONDARY_TO_PRIMARY);
|
||||
secondary->prepare_expire();
|
||||
OPENVPN_LOG_PROTO_VERBOSE("*** PROMOTE_SECONDARY_TO_PRIMARY pri=" << primary->key_id() << " sec=" << secondary->key_id());
|
||||
OPENVPN_LOG_PROTO_VERBOSE(debug_prefix() << " PROMOTE_SECONDARY_TO_PRIMARY");
|
||||
}
|
||||
|
||||
void process_primary_event()
|
||||
@ -2766,11 +2924,12 @@ namespace openvpn {
|
||||
switch (ev)
|
||||
{
|
||||
case KeyContext::KEV_ACTIVE:
|
||||
OPENVPN_LOG_PROTO_VERBOSE("*** SESSION_ACTIVE");
|
||||
OPENVPN_LOG_PROTO_VERBOSE(debug_prefix() << " SESSION_ACTIVE");
|
||||
primary->rekey(CryptoDCInstance::ACTIVATE_PRIMARY);
|
||||
active();
|
||||
break;
|
||||
case KeyContext::KEV_RENEGOTIATE:
|
||||
case KeyContext::KEV_RENEGOTIATE_FORCE:
|
||||
renegotiate();
|
||||
break;
|
||||
case KeyContext::KEV_EXPIRE:
|
||||
@ -2782,7 +2941,7 @@ namespace openvpn {
|
||||
disconnect(Error::PRIMARY_EXPIRE); // primary context expired and no secondary context available
|
||||
}
|
||||
break;
|
||||
case KeyContext::KEV_NEGOTIATE_FAILED:
|
||||
case KeyContext::KEV_NEGOTIATE:
|
||||
stats->error(Error::HANDSHAKE_TIMEOUT);
|
||||
disconnect(Error::HANDSHAKE_TIMEOUT); // primary negotiation failed
|
||||
break;
|
||||
@ -2790,6 +2949,7 @@ namespace openvpn {
|
||||
break;
|
||||
}
|
||||
}
|
||||
primary->set_next_event_if_unspecified();
|
||||
}
|
||||
|
||||
void process_secondary_event()
|
||||
@ -2812,16 +2972,40 @@ namespace openvpn {
|
||||
secondary->rekey(CryptoDCInstance::DEACTIVATE_SECONDARY);
|
||||
secondary.reset();
|
||||
break;
|
||||
case KeyContext::KEV_NEGOTIATE_FAILED:
|
||||
case KeyContext::KEV_RENEGOTIATE_QUEUE:
|
||||
primary->key_limit_reneg(KeyContext::KEV_RENEGOTIATE_FORCE, secondary->become_primary_time());
|
||||
break;
|
||||
case KeyContext::KEV_NEGOTIATE:
|
||||
stats->error(Error::HANDSHAKE_TIMEOUT);
|
||||
case KeyContext::KEV_PRIMARY_PENDING:
|
||||
case KeyContext::KEV_RENEGOTIATE_FORCE:
|
||||
renegotiate();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (secondary)
|
||||
secondary->set_next_event_if_unspecified();
|
||||
}
|
||||
|
||||
#ifdef OPENVPN_INSTRUMENTATION
|
||||
std::string debug_prefix()
|
||||
{
|
||||
std::string ret = openvpn::to_string(now_->raw());
|
||||
ret += is_server() ? " SERVER[" : " CLIENT[";
|
||||
if (primary)
|
||||
ret += openvpn::to_string(primary->key_id());
|
||||
if (secondary)
|
||||
{
|
||||
ret += '/';
|
||||
ret += openvpn::to_string(secondary->key_id());
|
||||
}
|
||||
ret += ']';
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
// key_id starts at 0, increments to KEY_ID_MASK, then recycles back to 1.
|
||||
// Therefore, if key_id is 0, it is the first key.
|
||||
unsigned int next_key_id()
|
||||
@ -2876,8 +3060,6 @@ namespace openvpn {
|
||||
KeyContext::Ptr secondary;
|
||||
bool dc_deferred;
|
||||
|
||||
bool fast_transition;
|
||||
|
||||
// END ProtoContext data members
|
||||
};
|
||||
|
||||
|
@ -91,11 +91,13 @@ namespace openvpn {
|
||||
|
||||
ProtoStackBase(SSLFactoryAPI& ssl_factory, // SSL factory object that can be used to generate new SSL sessions
|
||||
TimePtr now_arg, // pointer to current time
|
||||
const Time::Duration& tls_timeout_arg, // packet retransmit timeout
|
||||
const Frame::Ptr& frame, // contains info on how to allocate and align buffers
|
||||
const SessionStats::Ptr& stats_arg, // error statistics
|
||||
const id_t span, // basically the window size for our reliability layer
|
||||
const size_t max_ack_list) // maximum number of ACK messages to bundle in one packet
|
||||
: ssl_(ssl_factory.ssl()),
|
||||
: tls_timeout(tls_timeout_arg),
|
||||
ssl_(ssl_factory.ssl()),
|
||||
frame_(frame),
|
||||
up_stack_reentry_level(0),
|
||||
invalidated_(false),
|
||||
@ -190,7 +192,7 @@ namespace openvpn {
|
||||
if (m.ready_retransmit(*now))
|
||||
{
|
||||
parent().net_send(m.packet, NET_SEND_RETRANSMIT);
|
||||
m.reset_retransmit(*now);
|
||||
m.reset_retransmit(*now, tls_timeout);
|
||||
}
|
||||
}
|
||||
update_retransmit();
|
||||
@ -326,7 +328,7 @@ namespace openvpn {
|
||||
// encapsulate SSL ciphertext packets
|
||||
while (ssl_->read_ciphertext_ready() && rel_send.ready())
|
||||
{
|
||||
typename ReliableSend::Message& m = rel_send.send(*now);
|
||||
typename ReliableSend::Message& m = rel_send.send(*now, tls_timeout);
|
||||
m.packet = PACKET(ssl_->read_ciphertext());
|
||||
|
||||
// encapsulate packet
|
||||
@ -350,7 +352,7 @@ namespace openvpn {
|
||||
{
|
||||
while (!raw_write_queue.empty() && rel_send.ready())
|
||||
{
|
||||
typename ReliableSend::Message& m = rel_send.send(*now);
|
||||
typename ReliableSend::Message& m = rel_send.send(*now, tls_timeout);
|
||||
m.packet = raw_write_queue.front();
|
||||
raw_write_queue.pop_front();
|
||||
|
||||
@ -453,6 +455,7 @@ namespace openvpn {
|
||||
}
|
||||
|
||||
private:
|
||||
const Time::Duration tls_timeout;
|
||||
typename SSLAPI::Ptr ssl_;
|
||||
Frame::Ptr frame_;
|
||||
int up_stack_reentry_level;
|
||||
|
@ -28,45 +28,62 @@
|
||||
|
||||
namespace openvpn {
|
||||
inline void set_duration_parm(Time::Duration& dur,
|
||||
const char *name,
|
||||
const std::string& name,
|
||||
const std::string& valstr,
|
||||
const unsigned int min_value,
|
||||
const bool x2)
|
||||
const bool x2, // multiply result by 2
|
||||
const bool ms) // values are in milliseconds rather than seconds
|
||||
{
|
||||
const unsigned int maxdur = 60*60*24*7; // maximum duration -- 7 days
|
||||
const unsigned int maxdur = ms ? 1000*60*60*24 : 60*60*24*7; // maximum duration -- milliseconds: 1 day, seconds: 7 days
|
||||
unsigned int value = 0;
|
||||
const bool status = parse_number<unsigned int>(valstr, value);
|
||||
if (!status)
|
||||
OPENVPN_THROW(option_error, name << ": error parsing number of seconds");
|
||||
OPENVPN_THROW(option_error, name << ": error parsing number of " << (ms ? "milliseconds" : "seconds"));
|
||||
if (x2)
|
||||
value *= 2;
|
||||
if (value == 0 || value > maxdur)
|
||||
value = maxdur;
|
||||
if (value < min_value)
|
||||
value = min_value;
|
||||
dur = Time::Duration::seconds(value);
|
||||
dur = ms ? Time::Duration::milliseconds(value) : Time::Duration::seconds(value);
|
||||
}
|
||||
|
||||
inline const Option* load_duration_parm(Time::Duration& dur,
|
||||
const char *name,
|
||||
const std::string& name,
|
||||
const OptionList& opt,
|
||||
const unsigned int min_value,
|
||||
const bool x2)
|
||||
const bool x2,
|
||||
const bool allow_ms)
|
||||
{
|
||||
const Option *o = opt.get_ptr(name);
|
||||
if (o)
|
||||
set_duration_parm(dur, name, o->get(1, 16), min_value, x2);
|
||||
return o;
|
||||
// look for milliseconds given as <name>-ms
|
||||
if (allow_ms)
|
||||
{
|
||||
const Option *o = opt.get_ptr(name + "-ms");
|
||||
if (o)
|
||||
{
|
||||
set_duration_parm(dur, name, o->get(1, 16), min_value, x2, true);
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
||||
// look for seconds given as <name>
|
||||
{
|
||||
const Option *o = opt.get_ptr(name);
|
||||
if (o)
|
||||
set_duration_parm(dur, name, o->get(1, 16), allow_ms ? 1 : min_value, x2, false);
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
||||
inline Time::Duration load_duration_default(const char *name,
|
||||
inline Time::Duration load_duration_default(const std::string& name,
|
||||
const OptionList& opt,
|
||||
const Time::Duration& default_duration,
|
||||
const unsigned int min_value,
|
||||
const bool x2)
|
||||
const bool x2,
|
||||
const bool allow_ms)
|
||||
{
|
||||
Time::Duration ret(default_duration);
|
||||
load_duration_parm(ret, name, opt, min_value, x2);
|
||||
load_duration_parm(ret, name, opt, min_value, x2, allow_ms);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include <openvpn/server/servhalt.hpp>
|
||||
#include <openvpn/server/peerstats.hpp>
|
||||
#include <openvpn/server/peeraddr.hpp>
|
||||
#include <openvpn/ssl/datalimit.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
@ -118,6 +119,12 @@ namespace openvpn {
|
||||
// client float notification
|
||||
virtual void float_notify(const PeerAddr::Ptr& addr) = 0;
|
||||
|
||||
// Data limit notification -- trigger a renegotiation
|
||||
// when cdl_status == DataLimit::Red.
|
||||
virtual void data_limit_notify(const int key_id,
|
||||
const DataLimit::Mode cdl_mode,
|
||||
const DataLimit::State cdl_status) = 0;
|
||||
|
||||
// push a halt or restart message to client
|
||||
virtual void push_halt_restart_msg(const HaltRestart::Type type,
|
||||
const std::string& reason,
|
||||
|
@ -5,6 +5,7 @@ GCC_EXTRA="$GCC_EXTRA -DOPENVPN_SHOW_SESSION_TOKEN"
|
||||
[ "$EXIT" = "1" ] && GCC_EXTRA="$GCC_EXTRA -DTUN_NULL_EXIT"
|
||||
[ "$GREMLIN" = "1" ] && GCC_EXTRA="$GCC_EXTRA -DOPENVPN_GREMLIN"
|
||||
[ "$DEX" = "1" ] && GCC_EXTRA="$GCC_EXTRA -DOPENVPN_DISABLE_EXPLICIT_EXIT"
|
||||
[ "$BS64" = "1" ] && GCC_EXTRA="$GCC_EXTRA -DOPENVPN_BS64_DATA_LIMIT=2500000"
|
||||
if [ "$AGENT" = "1" ]; then
|
||||
GCC_EXTRA="$GCC_EXTRA -DOPENVPN_COMMAND_AGENT"
|
||||
export JSON=1
|
||||
|
@ -39,6 +39,51 @@
|
||||
#define OPENVPN_DEBUG
|
||||
#define OPENVPN_ENABLE_ASSERT
|
||||
#define USE_TLS_AUTH
|
||||
#define OPENVPN_INSTRUMENTATION
|
||||
|
||||
// Data limits for Blowfish and other 64-bit block-size ciphers
|
||||
#ifndef BF
|
||||
#define BF 0
|
||||
#endif
|
||||
#define OPENVPN_BS64_DATA_LIMIT 50000
|
||||
#if BF == 1
|
||||
#define PROTO_CIPHER "BF-CBC"
|
||||
#define TLS_VER_MIN TLSVersion::UNDEF
|
||||
#define HANDSHAKE_WINDOW 60
|
||||
#define BECOME_PRIMARY_CLIENT 5
|
||||
#define BECOME_PRIMARY_SERVER 5
|
||||
#define TLS_TIMEOUT_CLIENT 1000
|
||||
#define TLS_TIMEOUT_SERVER 1000
|
||||
#define FEEDBACK 0
|
||||
#elif BF == 2
|
||||
#define PROTO_CIPHER "BF-CBC"
|
||||
#define TLS_VER_MIN TLSVersion::UNDEF
|
||||
#define HANDSHAKE_WINDOW 10
|
||||
#define BECOME_PRIMARY_CLIENT 10
|
||||
#define BECOME_PRIMARY_SERVER 10
|
||||
#define TLS_TIMEOUT_CLIENT 2000
|
||||
#define TLS_TIMEOUT_SERVER 1000
|
||||
#define FEEDBACK 0
|
||||
#elif BF == 3
|
||||
#define PROTO_CIPHER "BF-CBC"
|
||||
#define TLS_VER_MIN TLSVersion::UNDEF
|
||||
#define HANDSHAKE_WINDOW 60
|
||||
#define BECOME_PRIMARY_CLIENT 60
|
||||
#define BECOME_PRIMARY_SERVER 10
|
||||
#define TLS_TIMEOUT_CLIENT 2000
|
||||
#define TLS_TIMEOUT_SERVER 1000
|
||||
#define FEEDBACK 0
|
||||
#elif BF != 0
|
||||
#error unknown BF value
|
||||
#endif
|
||||
|
||||
// TLS timeout
|
||||
#ifndef TLS_TIMEOUT_CLIENT
|
||||
#define TLS_TIMEOUT_CLIENT 2000
|
||||
#endif
|
||||
#ifndef TLS_TIMEOUT_SERVER
|
||||
#define TLS_TIMEOUT_SERVER 2000
|
||||
#endif
|
||||
|
||||
// NoisyWire
|
||||
#ifndef NOERR
|
||||
@ -52,6 +97,13 @@
|
||||
#define RENEG 900
|
||||
#endif
|
||||
|
||||
// feedback
|
||||
#ifndef FEEDBACK
|
||||
#define FEEDBACK 1
|
||||
#else
|
||||
#define FEEDBACK 0
|
||||
#endif
|
||||
|
||||
// number of threads to use for test
|
||||
#ifndef N_THREADS
|
||||
#define N_THREADS 1
|
||||
@ -87,17 +139,17 @@
|
||||
// setup cipher
|
||||
#ifndef PROTO_CIPHER
|
||||
#ifdef PROTOv2
|
||||
#define PROTO_CIPHER AES-256-GCM
|
||||
#define PROTO_CIPHER "AES-256-GCM"
|
||||
#define TLS_VER_MIN TLSVersion::V1_2
|
||||
#else
|
||||
#define PROTO_CIPHER AES-128-CBC
|
||||
#define PROTO_CIPHER "AES-128-CBC"
|
||||
#define TLS_VER_MIN TLSVersion::UNDEF
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// setup digest
|
||||
#ifndef PROTO_DIGEST
|
||||
#define PROTO_DIGEST SHA1
|
||||
#define PROTO_DIGEST "SHA1"
|
||||
#endif
|
||||
|
||||
// setup compressor
|
||||
@ -272,7 +324,7 @@ public:
|
||||
#if defined(VERBOSE) || defined(DROUGHT_LIMIT)
|
||||
{
|
||||
const unsigned int r = drought.raw();
|
||||
#ifdef VERBOSE
|
||||
#if defined(VERBOSE)
|
||||
std::cout << "*** Drought " << name << " has reached " << r << std::endl;
|
||||
#endif
|
||||
#ifdef DROUGHT_LIMIT
|
||||
@ -309,15 +361,14 @@ public:
|
||||
|
||||
typedef Base::PacketType PacketType;
|
||||
|
||||
OPENVPN_EXCEPTION(session_invalidated);
|
||||
|
||||
TestProto(const Base::Config::Ptr& config,
|
||||
const SessionStats::Ptr& stats)
|
||||
: Base(config, stats),
|
||||
control_drought("control", config->now),
|
||||
data_drought("data", config->now),
|
||||
frame(config->frame),
|
||||
app_bytes_(0),
|
||||
net_bytes_(0),
|
||||
data_bytes_(0)
|
||||
frame(config->frame)
|
||||
{
|
||||
// zero progress value
|
||||
std::memset(progress_, 0, 11);
|
||||
@ -332,7 +383,6 @@ public:
|
||||
void initial_app_send(const char *msg)
|
||||
{
|
||||
Base::start();
|
||||
|
||||
const size_t msglen = std::strlen(msg) + 1;
|
||||
BufferAllocated app_buf((unsigned char *)msg, msglen, 0);
|
||||
copy_progress(app_buf);
|
||||
@ -340,6 +390,28 @@ public:
|
||||
flush(true);
|
||||
}
|
||||
|
||||
void app_send_templ_init(const char *msg)
|
||||
{
|
||||
Base::start();
|
||||
const size_t msglen = std::strlen(msg) + 1;
|
||||
templ.reset(new BufferAllocated((unsigned char *)msg, msglen, 0));
|
||||
flush(true);
|
||||
}
|
||||
|
||||
void app_send_templ()
|
||||
{
|
||||
#if !FEEDBACK
|
||||
if (bool(iteration++ & 1) == is_server())
|
||||
{
|
||||
modmsg(templ);
|
||||
BufferAllocated app_buf(*templ);
|
||||
control_send(std::move(app_buf));
|
||||
flush(true);
|
||||
++n_control_send_;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool do_housekeeping()
|
||||
{
|
||||
if (now() >= Base::next_housekeeping())
|
||||
@ -390,6 +462,8 @@ public:
|
||||
size_t net_bytes() const { return net_bytes_; }
|
||||
size_t app_bytes() const { return app_bytes_; }
|
||||
size_t data_bytes() const { return data_bytes_; }
|
||||
size_t n_control_recv() const { return n_control_recv_; }
|
||||
size_t n_control_send() const { return n_control_send_; }
|
||||
|
||||
const char *progress() const { return progress_; }
|
||||
|
||||
@ -399,6 +473,12 @@ public:
|
||||
control_drought.event();
|
||||
}
|
||||
|
||||
void check_invalidated()
|
||||
{
|
||||
if (Base::invalidated())
|
||||
throw session_invalidated(Error::name(Base::invalidation_reason()));
|
||||
}
|
||||
|
||||
std::deque<BufferPtr> net_out;
|
||||
|
||||
DroughtMeasure control_drought;
|
||||
@ -425,9 +505,12 @@ private:
|
||||
std::cout << now().raw() << " " << mode().str() << " " << show << std::endl;
|
||||
}
|
||||
#endif
|
||||
#if FEEDBACK
|
||||
modmsg(work);
|
||||
control_send(std::move(work));
|
||||
#endif
|
||||
control_drought.event();
|
||||
++n_control_recv_;
|
||||
}
|
||||
|
||||
void copy_progress(Buffer& buf)
|
||||
@ -464,9 +547,13 @@ private:
|
||||
}
|
||||
|
||||
Frame::Ptr frame;
|
||||
size_t app_bytes_;
|
||||
size_t net_bytes_;
|
||||
size_t data_bytes_;
|
||||
size_t app_bytes_ = 0;
|
||||
size_t net_bytes_ = 0;
|
||||
size_t data_bytes_ = 0;
|
||||
size_t n_control_send_ = 0;
|
||||
size_t n_control_recv_ = 0;
|
||||
BufferPtr templ;
|
||||
size_t iteration = 0;
|
||||
char progress_[11];
|
||||
};
|
||||
|
||||
@ -522,8 +609,6 @@ private:
|
||||
class NoisyWire
|
||||
{
|
||||
public:
|
||||
OPENVPN_SIMPLE_EXCEPTION(session_invalidated);
|
||||
|
||||
NoisyWire(const std::string title_arg,
|
||||
TimePtr now_arg,
|
||||
RandomAPI& rand_arg,
|
||||
@ -543,8 +628,8 @@ public:
|
||||
void xfer(T1& a, T2& b)
|
||||
{
|
||||
// check for errors
|
||||
if (a.invalidated() || b.invalidated())
|
||||
throw session_invalidated();
|
||||
a.check_invalidated();
|
||||
b.check_invalidated();
|
||||
|
||||
// need to retransmit?
|
||||
if (a.do_housekeeping())
|
||||
@ -554,10 +639,13 @@ public:
|
||||
#endif
|
||||
}
|
||||
|
||||
// queue a control channel packet
|
||||
a.app_send_templ();
|
||||
|
||||
// queue a data channel packet
|
||||
if (a.data_channel_ready())
|
||||
{
|
||||
BufferPtr bp = a.data_encrypt_string("Waiting for godot...");
|
||||
BufferPtr bp = a.data_encrypt_string("Waiting for godot A... Waiting for godot B... Waiting for godot C... Waiting for godot D... Waiting for godot E... Waiting for godot F... Waiting for godot G... Waiting for godot H... Waiting for godot I... Waiting for godot J...");
|
||||
wire.push_back(bp);
|
||||
}
|
||||
|
||||
@ -594,7 +682,7 @@ public:
|
||||
#ifdef VERBOSE
|
||||
if (bp->size())
|
||||
{
|
||||
const std::string show((char *)bp->data(), bp->size());
|
||||
const std::string show((char *)bp->data(), std::min(bp->size(), size_t(40)));
|
||||
std::cout << now->raw() << " " << title << " DATA CHANNEL DECRYPT: " << show << std::endl;
|
||||
}
|
||||
#endif
|
||||
@ -606,6 +694,13 @@ public:
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef VERBOSE
|
||||
std::cout << now->raw() << " " << title << " KEY_STATE_ERROR" << std::endl;
|
||||
#endif
|
||||
b.stat().error(Error::KEY_STATE_ERROR);
|
||||
}
|
||||
}
|
||||
b.flush(true);
|
||||
}
|
||||
@ -786,12 +881,12 @@ int test(const int thread_num)
|
||||
cp->remote_peer_id = 100;
|
||||
#endif
|
||||
cp->comp_ctx = CompressContext(COMP_METH, false);
|
||||
cp->dc.set_cipher(CryptoAlgs::lookup(STRINGIZE(PROTO_CIPHER)));
|
||||
cp->dc.set_digest(CryptoAlgs::lookup(STRINGIZE(PROTO_DIGEST)));
|
||||
cp->dc.set_cipher(CryptoAlgs::lookup(PROTO_CIPHER));
|
||||
cp->dc.set_digest(CryptoAlgs::lookup(PROTO_DIGEST));
|
||||
#ifdef USE_TLS_AUTH
|
||||
cp->tls_auth_factory.reset(new CryptoOvpnHMACFactory<ClientCryptoAPI>());
|
||||
cp->tls_auth_key.parse(tls_auth_key);
|
||||
cp->set_tls_auth_digest(CryptoAlgs::lookup(STRINGIZE(PROTO_DIGEST)));
|
||||
cp->set_tls_auth_digest(CryptoAlgs::lookup(PROTO_DIGEST));
|
||||
cp->key_direction = 0;
|
||||
#endif
|
||||
cp->reliable_window = 4;
|
||||
@ -804,7 +899,12 @@ int test(const int thread_num)
|
||||
#else
|
||||
cp->handshake_window = Time::Duration::seconds(18); // will cause a small number of handshake failures
|
||||
#endif
|
||||
cp->become_primary = Time::Duration::seconds(30);
|
||||
#ifdef BECOME_PRIMARY_CLIENT
|
||||
cp->become_primary = Time::Duration::seconds(BECOME_PRIMARY_CLIENT);
|
||||
#else
|
||||
cp->become_primary = cp->handshake_window;
|
||||
#endif
|
||||
cp->tls_timeout = Time::Duration::milliseconds(TLS_TIMEOUT_CLIENT);
|
||||
#if defined(CLIENT_NO_RENEG)
|
||||
cp->renegotiate = Time::Duration::infinite();
|
||||
#else
|
||||
@ -851,12 +951,12 @@ int test(const int thread_num)
|
||||
sp->remote_peer_id = 101;
|
||||
#endif
|
||||
sp->comp_ctx = CompressContext(COMP_METH, false);
|
||||
sp->dc.set_cipher(CryptoAlgs::lookup(STRINGIZE(PROTO_CIPHER)));
|
||||
sp->dc.set_digest(CryptoAlgs::lookup(STRINGIZE(PROTO_DIGEST)));
|
||||
sp->dc.set_cipher(CryptoAlgs::lookup(PROTO_CIPHER));
|
||||
sp->dc.set_digest(CryptoAlgs::lookup(PROTO_DIGEST));
|
||||
#ifdef USE_TLS_AUTH
|
||||
sp->tls_auth_factory.reset(new CryptoOvpnHMACFactory<ServerCryptoAPI>());
|
||||
sp->tls_auth_key.parse(tls_auth_key);
|
||||
sp->set_tls_auth_digest(CryptoAlgs::lookup(STRINGIZE(PROTO_DIGEST)));
|
||||
sp->set_tls_auth_digest(CryptoAlgs::lookup(PROTO_DIGEST));
|
||||
sp->key_direction = 1;
|
||||
#endif
|
||||
sp->reliable_window = 4;
|
||||
@ -869,7 +969,12 @@ int test(const int thread_num)
|
||||
#else
|
||||
sp->handshake_window = Time::Duration::seconds(17) + Time::Duration::binary_ms(512);
|
||||
#endif
|
||||
sp->become_primary = Time::Duration::seconds(30);
|
||||
#ifdef BECOME_PRIMARY_SERVER
|
||||
sp->become_primary = Time::Duration::seconds(BECOME_PRIMARY_SERVER);
|
||||
#else
|
||||
sp->become_primary = sp->handshake_window;
|
||||
#endif
|
||||
sp->tls_timeout = Time::Duration::milliseconds(TLS_TIMEOUT_SERVER);
|
||||
#if defined(SERVER_NO_RENEG)
|
||||
sp->renegotiate = Time::Duration::infinite();
|
||||
#else
|
||||
@ -907,9 +1012,14 @@ int test(const int thread_num)
|
||||
|
||||
int j = -1;
|
||||
try {
|
||||
#if FEEDBACK
|
||||
// start feedback loop
|
||||
cli_proto.initial_app_send(message);
|
||||
serv_proto.start();
|
||||
#else
|
||||
cli_proto.app_send_templ_init(message);
|
||||
serv_proto.app_send_templ_init(message);
|
||||
#endif
|
||||
|
||||
// message loop
|
||||
for (j = 0; j < ITER; ++j)
|
||||
@ -937,6 +1047,9 @@ int test(const int thread_num)
|
||||
<< " net_bytes=" << nb
|
||||
<< " data_bytes=" << db
|
||||
<< " prog=" << cli_proto.progress() << '/' << serv_proto.progress()
|
||||
#if !FEEDBACK
|
||||
<< " CTRL=" << cli_proto.n_control_recv() << '/' << cli_proto.n_control_send() << '/' << serv_proto.n_control_recv() << '/' << serv_proto.n_control_send()
|
||||
#endif
|
||||
<< " D=" << cli_proto.control_drought().raw() << '/' << cli_proto.data_drought().raw() << '/' << serv_proto.control_drought().raw() << '/' << serv_proto.data_drought().raw()
|
||||
<< " N=" << cli_proto.negotiations() << '/' << serv_proto.negotiations()
|
||||
<< " SH=" << cli_proto.slowest_handshake().raw() << '/' << serv_proto.slowest_handshake().raw()
|
||||
@ -948,6 +1061,10 @@ int test(const int thread_num)
|
||||
cli_stats->show_error_counts();
|
||||
std::cerr << "-------- SERVER STATS --------" << std::endl;
|
||||
serv_stats->show_error_counts();
|
||||
#endif
|
||||
#ifdef OPENVPN_MAX_DATALIMIT_BYTES
|
||||
std::cerr << "------------------------------" << std::endl;
|
||||
std::cerr << "MAX_DATALIMIT_BYTES=" << DataLimit::max_bytes() << std::endl;
|
||||
#endif
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
|
Loading…
Reference in New Issue
Block a user