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

This commit is contained in:
Sergey Abramchuk
2020-02-24 14:43:11 +03:00
655 changed files with 146468 additions and 0 deletions
+206
View File
@@ -0,0 +1,206 @@
// 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_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
@@ -0,0 +1,58 @@
// 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_SSL_IS_OPENVPN_PROTOCOL_H
#define OPENVPN_SSL_IS_OPENVPN_PROTOCOL_H
#include <algorithm> // for std::min
#include <openvpn/common/size.hpp> // for size_t
namespace openvpn {
// Peek at the first few bytes of a session and
// distinguishing between OpenVPN or SSL protocols.
inline bool is_openvpn_protocol(const unsigned char *p, const size_t len)
{
const int CONTROL_HARD_RESET_CLIENT_V2 = 7;
const int CONTROL_HARD_RESET_CLIENT_V3 = 10;
const int OPCODE_SHIFT = 3;
const int MIN_INITIAL_PKT_SIZE = 14;
switch (std::min(len, size_t(3)))
{
case 3:
return p[0] == 0
&& p[1] >= MIN_INITIAL_PKT_SIZE
&& (p[2] == (CONTROL_HARD_RESET_CLIENT_V2 << OPCODE_SHIFT)
|| p[2] == (CONTROL_HARD_RESET_CLIENT_V3 << OPCODE_SHIFT));
case 2:
return p[0] == 0 && p[1] >= MIN_INITIAL_PKT_SIZE;
case 1:
return p[0] == 0;
default:
return true;
}
}
}
#endif
+140
View File
@@ -0,0 +1,140 @@
// 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/>.
// Parse the remote-cert-tls, remote-cert-ku, and remote-cert-eku options.
#ifndef OPENVPN_SSL_KUPARSE_H
#define OPENVPN_SSL_KUPARSE_H
#include <vector>
#include <string>
#include <openvpn/common/size.hpp>
#include <openvpn/common/exception.hpp>
#include <openvpn/common/hexstr.hpp>
#include <openvpn/common/options.hpp>
namespace openvpn {
namespace KUParse {
enum TLSWebType {
TLS_WEB_NONE,
TLS_WEB_SERVER,
TLS_WEB_CLIENT,
};
inline void remote_cert_tls(const TLSWebType wt, std::vector<unsigned int>& ku, std::string& eku)
{
ku.clear();
eku = "";
switch (wt)
{
case TLS_WEB_NONE:
break;
case TLS_WEB_SERVER:
ku.clear();
ku.push_back(0xa0);
ku.push_back(0x88);
eku = "TLS Web Server Authentication";
break;
case TLS_WEB_CLIENT:
ku.clear();
ku.push_back(0x80);
ku.push_back(0x08);
ku.push_back(0x88);
eku = "TLS Web Client Authentication";
break;
}
}
inline TLSWebType remote_cert_type(const std::string& ct)
{
if (ct == "server")
return TLS_WEB_SERVER;
else if (ct == "client")
return TLS_WEB_CLIENT;
else
throw option_error("remote-cert-tls must be 'client' or 'server'");
}
inline void remote_cert_tls(const std::string& ct,
std::vector<unsigned int>& ku,
std::string& eku)
{
remote_cert_tls(remote_cert_type(ct), ku, eku);
}
inline void remote_cert_tls(const OptionList& opt,
const std::string& relay_prefix,
std::vector<unsigned int>& ku,
std::string& eku)
{
TLSWebType wt = TLS_WEB_NONE;
const Option* o = opt.get_ptr(relay_prefix + "remote-cert-tls");
if (o)
{
const std::string ct = o->get_optional(1, 16);
wt = remote_cert_type(ct);
}
remote_cert_tls(wt, ku, eku);
}
inline void remote_cert_ku(const OptionList& opt,
const std::string& relay_prefix,
std::vector<unsigned int>& ku)
{
ku.clear();
const Option* o = opt.get_ptr(relay_prefix + "remote-cert-ku");
if (o)
{
if (o->empty())
throw option_error("remote-cert-ku: no hex values specified");
else if (o->size() >= 64)
throw option_error("remote-cert-ku: too many parameters");
else
{
try {
for (size_t i = 1; i < o->size(); ++i)
ku.push_back(parse_hex_number<unsigned int>(o->get(i, 16)));
}
catch (parse_hex_error&)
{
throw option_error("remote-cert-ku: error parsing hex value list");
}
}
}
}
inline void remote_cert_eku(const OptionList& opt,
const std::string& relay_prefix,
std::string& eku)
{
eku = "";
const Option* o = opt.get_ptr(relay_prefix + "remote-cert-eku");
if (o)
eku = o->get(1, 256);
}
}
}
#endif
+68
View File
@@ -0,0 +1,68 @@
// 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_SSL_MSSPARMS_H
#define OPENVPN_SSL_MSSPARMS_H
#include <openvpn/common/options.hpp>
#include <openvpn/common/number.hpp>
namespace openvpn {
struct MSSParms
{
MSSParms() : mssfix(0),
mtu(false)
{
}
void parse(const OptionList& opt)
{
const Option *o = opt.get_ptr("mssfix");
if (o)
{
const bool status = parse_number_validate<decltype(mssfix)>(o->get(1, 16),
16,
576,
65535,
&mssfix);
if (!status)
throw option_error("mssfix: parse/range issue");
mtu = (o->get_optional(2, 16) == "mtu");
}
}
unsigned int mssfix; // standard OpenVPN mssfix parm
bool mtu; // consider transport packet overhead in MSS adjustment
};
struct MSSCtrlParms
{
MSSCtrlParms(const OptionList& opt)
{
mssfix_ctrl = opt.get_num<decltype(mssfix_ctrl)>("mssfix-ctrl", 1, 1250,
256, 65535);
}
unsigned int mssfix_ctrl;
};
}
#endif
+64
View File
@@ -0,0 +1,64 @@
// 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/>.
// Parse the ns-cert-type option.
#ifndef OPENVPN_SSL_NSCERT_H
#define OPENVPN_SSL_NSCERT_H
#include <string>
#include <openvpn/common/size.hpp>
#include <openvpn/common/exception.hpp>
#include <openvpn/common/options.hpp>
namespace openvpn {
namespace NSCert {
enum Type {
NONE,
CLIENT,
SERVER
};
inline Type ns_cert_type(const std::string& ct)
{
if (ct == "server")
return SERVER;
else if (ct == "client")
return CLIENT;
else
throw option_error("ns-cert-type must be 'client' or 'server'");
}
inline Type ns_cert_type(const OptionList& opt, const std::string& relay_prefix)
{
const Option* o = opt.get_ptr(relay_prefix + "ns-cert-type");
if (o)
{
const std::string ct = o->get_optional(1, 16);
return ns_cert_type(ct);
}
return NONE;
}
}
}
#endif
+128
View File
@@ -0,0 +1,128 @@
// 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/>.
// These objects are primary concerned with generating the Peer Info on the
// client side before transmission to server. For the reverse case (parsing
// the Peer Info on the server) we normally use an OptionList.
#ifndef OPENVPN_SSL_PEERINFO_H
#define OPENVPN_SSL_PEERINFO_H
#include <string>
#include <vector>
#include <openvpn/common/rc.hpp>
#include <openvpn/common/string.hpp>
#include <openvpn/common/split.hpp>
#include <openvpn/common/unicode.hpp>
namespace openvpn {
namespace PeerInfo {
OPENVPN_EXCEPTION(peer_info_error);
struct KeyValue
{
KeyValue(const std::string& key_arg, const std::string& value_arg)
: key(key_arg),
value(value_arg)
{
}
std::string key;
std::string value;
std::string to_string() const
{
return key + '=' + value;
}
};
struct Set : public std::vector<KeyValue>, public RCCopyable<thread_unsafe_refcount>
{
typedef RCPtr<Set> Ptr;
template <typename SET>
static Ptr new_from_foreign_set(const SET& other)
{
Ptr sp = new Set();
for (const auto &kv : other)
sp->emplace_back(kv.key, kv.value);
return sp;
}
template <typename SET>
void append_foreign_set_ptr(const SET* other)
{
if (other)
for (const auto &kv : *other)
emplace_back(kv.key, kv.value);
}
template <typename SET>
void append_foreign_set_ref(const SET& other)
{
for (const auto &kv : other)
emplace_back(kv.key, kv.value);
}
Ptr copy() const
{
return new Set(*this);
}
// Parse src in the form K1=V1,K2=V2,...
template <typename SET>
static void parse_csv(const std::string& src, SET& dest)
{
if (!string::is_empty(src))
{
if (string::is_multiline(src))
OPENVPN_THROW(peer_info_error, "key/value list must be a single line: " << Unicode::utf8_printable(src, 256));
const auto list = Split::by_char<std::vector<std::string>, StandardLex, Split::NullLimit>(src, ',', Split::TRIM_LEADING_SPACES|Split::TRIM_SPECIAL);
for (const auto &kvstr : list)
{
const auto kv = Split::by_char<std::vector<std::string>, StandardLex, Split::NullLimit>(kvstr, '=', 0, 1);
if (kv.size() == 2)
dest.emplace_back(kv[0], kv[1]);
else
OPENVPN_THROW(peer_info_error, "key/value must be in the form K=V, not: " << Unicode::utf8_printable(kvstr, 256));
}
}
}
std::string to_string() const
{
std::string ret;
ret.reserve(256);
for (const auto &kv : *this)
{
ret += kv.to_string();
ret += '\n';
}
return ret;
}
};
}
}
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,64 @@
// 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/>.
// Helper class to parse certain options needed by ProtoContext.
#ifndef OPENVPN_SSL_PROTO_CONTEXT_OPTIONS_H
#define OPENVPN_SSL_PROTO_CONTEXT_OPTIONS_H
#include <string>
#include <openvpn/common/exception.hpp>
#include <openvpn/common/options.hpp>
namespace openvpn {
struct ProtoContextOptions : public RC<thread_safe_refcount>
{
typedef RCPtr<ProtoContextOptions> Ptr;
enum CompressionMode {
COMPRESS_NO,
COMPRESS_YES,
COMPRESS_ASYM
};
ProtoContextOptions() : compression_mode(COMPRESS_NO) {}
bool is_comp() const { return compression_mode != COMPRESS_NO; }
bool is_comp_asym() const { return compression_mode == COMPRESS_ASYM; }
void parse_compression_mode(const std::string& mode)
{
if (mode == "no")
compression_mode = COMPRESS_NO;
else if (mode == "yes")
compression_mode = COMPRESS_YES;
else if (mode == "asym")
compression_mode = COMPRESS_ASYM;
else
OPENVPN_THROW(option_error, "error parsing compression mode: " << mode);
}
CompressionMode compression_mode;
};
}
#endif
+486
View File
@@ -0,0 +1,486 @@
// 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_SSL_PROTOSTACK_H
#define OPENVPN_SSL_PROTOSTACK_H
#include <deque>
#include <utility>
#include <openvpn/common/exception.hpp>
#include <openvpn/common/size.hpp>
#include <openvpn/common/usecount.hpp>
#include <openvpn/buffer/buffer.hpp>
#include <openvpn/time/time.hpp>
#include <openvpn/log/sessionstats.hpp>
#include <openvpn/reliable/relrecv.hpp>
#include <openvpn/reliable/relsend.hpp>
#include <openvpn/reliable/relack.hpp>
#include <openvpn/frame/frame.hpp>
#include <openvpn/error/excode.hpp>
#include <openvpn/ssl/sslconsts.hpp>
#include <openvpn/ssl/sslapi.hpp>
// ProtoStackBase is designed to allow general-purpose protocols (including
// but not limited to OpenVPN) to run over SSL, where the underlying transport
// layer is unreliable, such as UDP. The OpenVPN protocol implementation in
// proto.hpp (ProtoContext) layers on top of ProtoStackBase.
// ProtoStackBase is independent of any particular SSL implementation, and
// accepts the SSL object type as a template parameter.
namespace openvpn {
// PACKET type must define the following methods:
//
// Default constructor:
// PACKET()
//
// Constructor for BufferPtr:
// explicit PACKET(const BufferPtr& buf)
//
// Test if defined:
// operator bool() const
//
// Return true if packet is raw, or false if packet is SSL ciphertext:
// bool is_raw() const
//
// Reset back to post-default-constructor state:
// void reset()
//
// Return internal BufferPtr:
// const BufferPtr& buffer_ptr() const
//
// Call frame.prepare on internal buffer:
// void frame_prepare(const Frame& frame, const unsigned int context)
template <typename PACKET, typename PARENT>
class ProtoStackBase
{
public:
typedef reliable::id_t id_t;
typedef ReliableSendTemplate<PACKET> ReliableSend;
typedef ReliableRecvTemplate<PACKET> ReliableRecv;
OPENVPN_SIMPLE_EXCEPTION(proto_stack_invalidated);
OPENVPN_SIMPLE_EXCEPTION(unknown_status_from_ssl_layer);
enum NetSendType {
NET_SEND_SSL,
NET_SEND_RAW,
NET_SEND_ACK,
NET_SEND_RETRANSMIT,
};
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
: tls_timeout(tls_timeout_arg),
ssl_(ssl_factory.ssl()),
frame_(frame),
up_stack_reentry_level(0),
invalidated_(false),
invalidation_reason_(Error::SUCCESS),
ssl_started_(false),
next_retransmit_(Time::infinite()),
stats(stats_arg),
now(now_arg),
rel_recv(span),
rel_send(span),
xmit_acks(max_ack_list)
{
}
// Start SSL handshake on underlying SSL connection object.
void start_handshake()
{
if (!invalidated())
{
ssl_->start_handshake();
ssl_started_ = true;
up_sequenced();
}
}
uint32_t get_tls_warnings() const
{
return ssl_->get_tls_warnings();
}
// Incoming ciphertext packet arriving from network,
// we will take ownership of pkt.
bool net_recv(PACKET&& pkt)
{
if (!invalidated())
return up_stack(pkt);
return false;
}
// Outgoing application-level cleartext packet ready to send
// (will be encrypted via SSL), we will take ownership
// of buf.
void app_send(BufferPtr&& buf)
{
if (!invalidated())
app_write_queue.push_back(std::move(buf));
}
// Outgoing raw packet ready to send (will NOT be encrypted
// via SSL, but will still be encapsulated, sequentialized,
// and tracked via reliability layer).
void raw_send(PACKET&& pkt)
{
if (!invalidated())
raw_write_queue.push_back(std::move(pkt));
}
// Write any pending data to network and update retransmit
// timer. Should be called as a final step after one or more
// net_recv, app_send, raw_send, or start_handshake calls.
void flush()
{
if (!invalidated() && !up_stack_reentry_level)
{
down_stack_raw();
down_stack_app();
update_retransmit();
}
}
// Send pending ACKs back to sender for packets already received
void send_pending_acks()
{
if (!invalidated())
{
while (!xmit_acks.empty())
{
ack_send_buf.frame_prepare(*frame_, Frame::WRITE_ACK_STANDALONE);
// encapsulate standalone ACK
parent().generate_ack(ack_send_buf);
// transmit it
parent().net_send(ack_send_buf, NET_SEND_ACK);
}
}
}
// Send any pending retransmissions
void retransmit()
{
if (!invalidated() && *now >= next_retransmit_)
{
for (id_t i = rel_send.head_id(); i < rel_send.tail_id(); ++i)
{
typename ReliableSend::Message& m = rel_send.ref_by_id(i);
if (m.ready_retransmit(*now))
{
parent().net_send(m.packet, NET_SEND_RETRANSMIT);
m.reset_retransmit(*now, tls_timeout);
}
}
update_retransmit();
}
}
// When should we next call retransmit()
Time next_retransmit() const
{
if (!invalidated())
return next_retransmit_;
else
return Time::infinite();
}
// Has SSL handshake been started yet?
bool ssl_started() const { return ssl_started_; }
// Was session invalidated by an exception?
bool invalidated() const { return invalidated_; }
// Reason for invalidation
Error::Type invalidation_reason() const { return invalidation_reason_; }
// Invalidate session
void invalidate(const Error::Type reason)
{
if (!invalidated_)
{
invalidated_ = true;
invalidation_reason_ = reason;
parent().invalidate_callback();
}
}
std::string ssl_handshake_details() const
{
return ssl_->ssl_handshake_details();
}
const AuthCert::Ptr& auth_cert() const
{
return ssl_->auth_cert();
}
private:
// Parent methods -- derived class must define these methods
// Encapsulate packet, use id as sequence number. If xmit_acks is non-empty,
// try to piggy-back ACK replies from xmit_acks to sender in encapsulated
// packet. Any exceptions thrown will invalidate session, i.e. this object
// can no longer be used.
//
// void encapsulate(id_t id, PACKET& pkt) = 0;
// Perform integrity check on packet. If packet is good, unencapsulate it and
// pass it into the rel_recv object. Any ACKs received for messages previously
// sent should be marked in rel_send. Message sequence number should be recorded
// in xmit_acks. Exceptions may be thrown here and they will be passed up to
// caller of net_recv and will not invalidate the session.
// Method should return true if packet was placed into rel_recv.
//
// bool decapsulate(PACKET& pkt) = 0;
// Generate a standalone ACK message in buf based on ACKs in xmit_acks
// (PACKET will be already be initialized by frame_prepare()).
//
// void generate_ack(PACKET& pkt) = 0;
// Transmit encapsulated ciphertext packet to peer. Method may not modify
// or take ownership of net_pkt or underlying data unless it copies it.
//
// void net_send(const PACKET& net_pkt, const NetSendType nstype) = 0;
// Pass cleartext data up to application, which make take
// ownership of to_app_buf via std::move.
//
// void app_recv(BufferPtr&& to_app_buf) = 0;
// Pass raw data up to application. A packet is considered to be raw
// if is_raw() method returns true. Method may take ownership
// of raw_pkt via std::move.
//
// void raw_recv(PACKET&& raw_pkt) = 0;
// called if session is invalidated by an error (optional)
//
// void invalidate_callback() {}
// END of parent methods
// get reference to parent for CRTP
PARENT& parent()
{
return *static_cast<PARENT*>(this);
}
// app data -> SSL -> protocol encapsulation -> reliability layer -> network
void down_stack_app()
{
if (ssl_started_)
{
// push app-layer cleartext through SSL object
while (!app_write_queue.empty())
{
BufferPtr& buf = app_write_queue.front();
ssize_t size;
try {
size = ssl_->write_cleartext_unbuffered(buf->data(), buf->size());
}
catch (...)
{
error(Error::SSL_ERROR);
throw;
}
if (size == buf->size())
app_write_queue.pop_front();
else if (size == SSLConst::SHOULD_RETRY)
break;
else if (size >= 0)
{
// partial write
app_write_queue.front()->advance(size);
break;
}
else
{
error(Error::SSL_ERROR);
throw unknown_status_from_ssl_layer();
}
}
// encapsulate SSL ciphertext packets
while (ssl_->read_ciphertext_ready() && rel_send.ready())
{
typename ReliableSend::Message& m = rel_send.send(*now, tls_timeout);
m.packet = PACKET(ssl_->read_ciphertext());
// encapsulate packet
try {
parent().encapsulate(m.id(), m.packet);
}
catch (...)
{
error(Error::ENCAPSULATION_ERROR);
throw;
}
// transmit it
parent().net_send(m.packet, NET_SEND_SSL);
}
}
}
// raw app data -> protocol encapsulation -> reliability layer -> network
void down_stack_raw()
{
while (!raw_write_queue.empty() && rel_send.ready())
{
typename ReliableSend::Message& m = rel_send.send(*now, tls_timeout);
m.packet = raw_write_queue.front();
raw_write_queue.pop_front();
// encapsulate packet
try {
parent().encapsulate(m.id(), m.packet);
}
catch (...)
{
error(Error::ENCAPSULATION_ERROR);
throw;
}
// transmit it
parent().net_send(m.packet, NET_SEND_RAW);
}
}
// network -> reliability layer -> protocol decapsulation -> SSL -> app
bool up_stack(PACKET& recv)
{
UseCount use_count(up_stack_reentry_level);
if (parent().decapsulate(recv))
{
up_sequenced();
return true;
}
else
return false;
}
// if a sequenced packet is available from reliability layer,
// move it up the stack
void up_sequenced()
{
// is sequenced receive packet available?
while (rel_recv.ready())
{
typename ReliableRecv::Message& m = rel_recv.next_sequenced();
if (m.packet.is_raw())
parent().raw_recv(std::move(m.packet));
else // SSL packet
{
if (ssl_started_)
ssl_->write_ciphertext(m.packet.buffer_ptr());
else
break;
}
rel_recv.advance();
}
// read cleartext data from SSL object
if (ssl_started_)
while (ssl_->read_cleartext_ready())
{
ssize_t size;
to_app_buf.reset(new BufferAllocated());
frame_->prepare(Frame::READ_SSL_CLEARTEXT, *to_app_buf);
try {
size = ssl_->read_cleartext(to_app_buf->data(), to_app_buf->max_size());
}
catch (...)
{
// SSL fatal errors will invalidate the session
error(Error::SSL_ERROR);
throw;
}
if (size >= 0)
{
to_app_buf->set_size(size);
// pass cleartext data to app
parent().app_recv(std::move(to_app_buf));
}
else if (size == SSLConst::SHOULD_RETRY)
break;
else if (size == SSLConst::PEER_CLOSE_NOTIFY)
{
error(Error::SSL_ERROR);
throw ErrorCode(Error::CLIENT_HALT, true, "SSL Close Notify received");
}
else
{
error(Error::SSL_ERROR);
throw unknown_status_from_ssl_layer();
}
}
}
void update_retransmit()
{
next_retransmit_ = *now + rel_send.until_retransmit(*now);
}
void error(const Error::Type reason)
{
if (stats)
stats->error(reason);
invalidate(reason);
}
private:
const Time::Duration tls_timeout;
typename SSLAPI::Ptr ssl_;
Frame::Ptr frame_;
int up_stack_reentry_level;
bool invalidated_;
Error::Type invalidation_reason_;
bool ssl_started_;
Time next_retransmit_;
BufferPtr to_app_buf; // cleartext data decrypted by SSL that is to be passed to app via app_recv method
PACKET ack_send_buf; // only used for standalone ACKs to be sent to peer
std::deque<BufferPtr> app_write_queue;
std::deque<PACKET> raw_write_queue;
SessionStats::Ptr stats;
protected:
TimePtr now;
ReliableRecv rel_recv;
ReliableSend rel_send;
ReliableAck xmit_acks;
};
} // namespace openvpn
#endif // OPENVPN_SSL_PROTOSTACK_H
+108
View File
@@ -0,0 +1,108 @@
// 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/>.
// A 64-bit session ID, used by ProtoContext.
#ifndef OPENVPN_SSL_PSID_H
#define OPENVPN_SSL_PSID_H
#include <string>
#include <cstring>
#include <openvpn/buffer/buffer.hpp>
#include <openvpn/common/hexstr.hpp>
#include <openvpn/common/memneq.hpp>
namespace openvpn {
class ProtoSessionID
{
public:
enum {
SIZE=8
};
ProtoSessionID()
{
reset();
}
void reset()
{
defined_ = false;
std::memset(id_, 0, SIZE);
}
explicit ProtoSessionID(Buffer& buf)
{
buf.read(id_, SIZE);
defined_ = true;
}
template <typename PRNG_TYPE>
void randomize(PRNG_TYPE& prng)
{
prng.assert_crypto();
prng.rand_bytes(id_, SIZE);
defined_ = true;
}
void read(Buffer& buf)
{
buf.read(id_, SIZE);
defined_ = true;
}
void write(Buffer& buf) const
{
buf.write(id_, SIZE);
}
void prepend(Buffer& buf) const
{
buf.prepend(id_, SIZE);
}
bool defined() const { return defined_; }
bool match(const ProtoSessionID& other) const
{
return defined_ && other.defined_ && !crypto::memneq(id_, other.id_, SIZE);
}
std::string str() const
{
return render_hex(id_, SIZE);
}
protected:
ProtoSessionID(const unsigned char *data)
{
std::memcpy(id_, data, SIZE);
}
private:
bool defined_;
unsigned char id_[SIZE];
};
} // namespace openvpn
#endif // OPENVPN_SSL_PSID_H
@@ -0,0 +1,242 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2019 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#pragma once
#include <cstring>
#include <memory>
#include <openvpn/common/exception.hpp>
#include <openvpn/common/hash.hpp>
#include <openvpn/common/base64.hpp>
#include <openvpn/common/string.hpp>
#include <openvpn/buffer/buffer.hpp>
#include <openvpn/random/randapi.hpp>
namespace openvpn {
class OpenSSLContext;
class MbedTLSContext;
// Abstract base class used to provide an interface for TLS
// Session Ticket keying originally described by RFC 5077.
class TLSSessionTicketBase
{
public:
typedef std::unique_ptr<TLSSessionTicketBase> UPtr;
OPENVPN_EXCEPTION(sess_ticket_error);
enum Status {
NO_TICKET,
TICKET_AVAILABLE,
TICKET_EXPIRING,
};
class Name
{
public:
static constexpr size_t SIZE = 16;
explicit Name(RandomAPI& rng)
{
rng.rand_bytes(value_, SIZE);
}
explicit Name(const std::string& name_b64)
{
b64_to_key(name_b64, "key name", value_, SIZE);
}
explicit Name(const unsigned char name[SIZE])
{
std::memcpy(value_, name, SIZE);
}
bool operator==(const Name& rhs) const
{
return std::memcmp(value_, rhs.value_, SIZE) == 0;
}
bool operator!=(const Name& rhs) const
{
return std::memcmp(value_, rhs.value_, SIZE) != 0;
}
bool operator<(const Name& rhs) const
{
return std::memcmp(value_, rhs.value_, SIZE) < 0;
}
std::string to_string() const
{
return "TLSTicketName[" + b64() + ']';
}
std::string b64() const
{
return base64->encode(value_, SIZE);
}
template <typename HASH>
void hash(HASH& h) const
{
h(value_, SIZE);
}
#ifdef HAVE_CITYHASH
std::size_t hashval() const
{
HashSizeT h;
hash(h);
return h.value();
}
#endif
private:
// we need to friend SSL implementation classes
friend class OpenSSLContext;
friend class MbedTLSContext;
Name() {} // note that default constructor leaves object in an undefined state
unsigned char value_[SIZE];
};
class Key
{
public:
static constexpr size_t CIPHER_KEY_SIZE = 32;
static constexpr size_t HMAC_KEY_SIZE = 16;
explicit Key(RandomAPI& rng)
{
rng.assert_crypto();
rng.rand_bytes(cipher_value_, CIPHER_KEY_SIZE);
rng.rand_bytes(hmac_value_, HMAC_KEY_SIZE);
}
explicit Key(const std::string& cipher_key_b64, const std::string& hmac_key_b64)
{
b64_to_key(cipher_key_b64, "cipher key", cipher_value_, CIPHER_KEY_SIZE);
b64_to_key(hmac_key_b64, "hmac key", hmac_value_, HMAC_KEY_SIZE);
}
~Key()
{
// wipe keys
std::memset(cipher_value_, 0, CIPHER_KEY_SIZE);
std::memset(hmac_value_, 0, HMAC_KEY_SIZE);
}
std::string to_string() const
{
return "TLSTicketKey[cipher=" + cipher_b64() + " hmac=" + hmac_b64() + ']';
}
std::string cipher_b64() const
{
return base64->encode(cipher_value_, CIPHER_KEY_SIZE);
}
std::string hmac_b64() const
{
return base64->encode(hmac_value_, HMAC_KEY_SIZE);
}
bool operator==(const Key& rhs) const
{
return std::memcmp(cipher_value_, rhs.cipher_value_, CIPHER_KEY_SIZE) == 0 && std::memcmp(hmac_value_, rhs.hmac_value_, HMAC_KEY_SIZE) == 0;
}
bool operator!=(const Key& rhs) const
{
return !operator==(rhs);
}
template <typename KEY_TRANSFORM>
void key_transform(KEY_TRANSFORM& t)
{
unsigned char out[KEY_TRANSFORM::MAX_HMAC_SIZE];
// cipher
{
t.cipher_transform.reset();
t.cipher_transform.update(cipher_value_, CIPHER_KEY_SIZE);
const size_t size = t.cipher_transform.final(out);
if (size < CIPHER_KEY_SIZE)
throw sess_ticket_error("insufficient key material for cipher transform");
std::memcpy(cipher_value_, out, CIPHER_KEY_SIZE);
}
// hmac
{
t.hmac_transform.reset();
t.hmac_transform.update(hmac_value_, HMAC_KEY_SIZE);
const size_t size = t.hmac_transform.final(out);
if (size < HMAC_KEY_SIZE)
throw sess_ticket_error("insufficient key material for hmac transform");
std::memcpy(hmac_value_, out, HMAC_KEY_SIZE);
}
}
private:
// we need to friend SSL implementation classes
friend class OpenSSLContext;
friend class MbedTLSContext;
Key() {} // note that default constructor leaves object in an undefined state
unsigned char cipher_value_[CIPHER_KEY_SIZE];
unsigned char hmac_value_[HMAC_KEY_SIZE];
};
// method returns name and key
virtual Status create_session_ticket_key(Name& name, Key& key) const = 0;
// method is given name and returns key
virtual Status lookup_session_ticket_key(const Name& name, Key& key) const = 0;
// return string that identifies the app
virtual std::string session_id_context() const = 0;
virtual ~TLSSessionTicketBase() {}
private:
static void b64_to_key(const std::string& b64, const char *title, unsigned char *out, const size_t outlen)
{
Buffer srcbuf(out, outlen, false);
try {
base64->decode(srcbuf, b64);
}
catch (const std::exception& e)
{
throw sess_ticket_error(std::string("base64 decode for ") + title + ": " + std::string(e.what()));
}
if (srcbuf.size() != outlen)
throw sess_ticket_error(std::string("wrong input size for ") + title + ", actual=" + std::to_string(srcbuf.size()) + " expected=" + std::to_string(outlen));
}
};
}
#ifdef HAVE_CITYHASH
OPENVPN_HASH_METHOD(openvpn::TLSSessionTicketBase::Name, hashval);
#endif
@@ -0,0 +1,52 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2019 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#pragma once
#include <string>
#include <memory>
#include <openvpn/ssl/sslapi.hpp>
#include <openvpn/ssl/sni_metadata.hpp>
namespace openvpn {
namespace SNI {
// Abstract base class used to provide an SNI handler
class HandlerBase
{
public:
typedef std::unique_ptr<HandlerBase> UPtr;
// Return a new SSLFactoryAPI for this SNI name.
// Implementation may also set sni_metadata.
// Return SSLFactoryAPI::Ptr() if sni_name is not recognized.
// The caller guarantees that sni_name is valid UTF-8 and
// doesn't contain any control characters.
virtual SSLFactoryAPI::Ptr sni_hello(const std::string& sni_name,
SNI::Metadata::UPtr& sni_metadata,
SSLConfigAPI::Ptr default_factory) const = 0;
virtual ~HandlerBase() {}
};
}
}
@@ -0,0 +1,44 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2019 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#pragma once
#include <string>
#include <memory>
namespace openvpn {
class AuthCert;
namespace SNI {
class Metadata
{
public:
typedef std::unique_ptr<Metadata> UPtr;
virtual std::string sni_client_name(const AuthCert& ac) const = 0;
virtual ~Metadata() = default;
};
}
}
+202
View File
@@ -0,0 +1,202 @@
// 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/>.
// API for SSL implementations
#ifndef OPENVPN_SSL_SSLAPI_H
#define OPENVPN_SSL_SSLAPI_H
#include <string>
#include <cstdint>
#include <openvpn/common/size.hpp>
#include <openvpn/common/exception.hpp>
#include <openvpn/common/rc.hpp>
#include <openvpn/common/options.hpp>
#include <openvpn/common/mode.hpp>
#include <openvpn/common/jsonlib.hpp>
#include <openvpn/buffer/buffer.hpp>
#include <openvpn/frame/frame.hpp>
#include <openvpn/auth/authcert.hpp>
#include <openvpn/pki/epkibase.hpp>
#include <openvpn/pki/pktype.hpp>
#include <openvpn/ssl/kuparse.hpp>
#include <openvpn/ssl/nscert.hpp>
#include <openvpn/ssl/tlsver.hpp>
#include <openvpn/ssl/tls_remote.hpp>
#include <openvpn/ssl/tls_cert_profile.hpp>
#include <openvpn/ssl/sess_ticket.hpp>
#include <openvpn/random/randapi.hpp>
namespace openvpn {
namespace SNI {
class HandlerBase;
}
class SSLAPI : public RC<thread_unsafe_refcount>
{
public:
enum TLSWarnings {
TLS_WARN_SIG_MD5 = (1 << 0),
TLS_WARN_NAME_CONSTRAINTS = (1 << 1)
};
typedef RCPtr<SSLAPI> Ptr;
virtual void start_handshake() = 0;
virtual ssize_t write_cleartext_unbuffered(const void *data, const size_t size) = 0;
virtual ssize_t read_cleartext(void *data, const size_t capacity) = 0;
virtual bool read_cleartext_ready() const = 0;
virtual void write_ciphertext(const BufferPtr& buf) = 0;
virtual void write_ciphertext_unbuffered(const unsigned char *data, const size_t size) = 0;
virtual bool read_ciphertext_ready() const = 0;
virtual BufferPtr read_ciphertext() = 0;
virtual std::string ssl_handshake_details() const = 0;
virtual bool did_full_handshake() = 0;
virtual const AuthCert::Ptr& auth_cert() const = 0;
virtual void mark_no_cache() = 0; // prevent caching of client-side session (only meaningful when client_session_tickets is enabled)
uint32_t get_tls_warnings() const
{
return tls_warnings;
}
protected:
uint32_t tls_warnings = 0; // bitfield of SSLAPI::TLSWarnings
};
class SSLFactoryAPI : public RC<thread_unsafe_refcount>
{
public:
OPENVPN_EXCEPTION(ssl_options_error);
OPENVPN_EXCEPTION(ssl_context_error);
OPENVPN_EXCEPTION(ssl_external_pki);
OPENVPN_SIMPLE_EXCEPTION(ssl_ciphertext_in_overflow);
typedef RCPtr<SSLFactoryAPI> Ptr;
// create a new SSLAPI instance
virtual SSLAPI::Ptr ssl() = 0;
// like ssl() above but optionally verify hostname against cert CommonName and/or
// SubjectAltName, and optionally set/lookup a cache key for this session.
virtual SSLAPI::Ptr ssl(const std::string* hostname, const std::string* cache_key) = 0;
// client or server?
virtual const Mode& mode() const = 0;
};
class SSLConfigAPI : public RC<thread_unsafe_refcount>
{
public:
typedef RCPtr<SSLConfigAPI> Ptr;
enum LoadFlags {
LF_PARSE_MODE = (1<<0),
LF_ALLOW_CLIENT_CERT_NOT_REQUIRED = (1<<1),
LF_RELAY_MODE = (1<<2), // look for "relay-ca" instead of "ca" directive
LF_ALLOW_NAME_CONSTRAINTS = (1<<3) // do not fail on Name Constraints ext and drop a warning to UI
};
std::string private_key_type_string() const
{
PKType::Type type = private_key_type();
switch (type)
{
case PKType::PK_NONE:
return "None";
case PKType::PK_DSA:
return "DSA";
case PKType::PK_RSA:
return "RSA";
case PKType::PK_EC:
return "EC";
case PKType::PK_ECDSA:
return "ECDSA";
case PKType::PK_UNKNOWN:
default:
return "Unknown";
}
}
virtual void set_mode(const Mode& mode_arg) = 0;
virtual const Mode& get_mode() const = 0;
virtual void set_external_pki_callback(ExternalPKIBase* external_pki_arg) = 0; // private key alternative
virtual void set_session_ticket_handler(TLSSessionTicketBase* session_ticket_handler) = 0; // server side
virtual void set_client_session_tickets(const bool v) = 0; // client side
virtual void set_sni_handler(SNI::HandlerBase* sni_handler) = 0; // server side
virtual void set_sni_name(const std::string& sni_name_arg) = 0; // client side
virtual void set_private_key_password(const std::string& pwd) = 0;
virtual void load_ca(const std::string& ca_txt, bool strict) = 0;
virtual void load_crl(const std::string& crl_txt) = 0;
virtual void load_cert(const std::string& cert_txt) = 0;
virtual void load_cert(const std::string& cert_txt, const std::string& extra_certs_txt) = 0;
virtual void load_private_key(const std::string& key_txt) = 0;
virtual void load_dh(const std::string& dh_txt) = 0;
virtual std::string extract_ca() const = 0;
virtual std::string extract_crl() const = 0;
virtual std::string extract_cert() const = 0;
virtual std::vector<std::string> extract_extra_certs() const = 0;
virtual std::string extract_private_key() const = 0;
virtual std::string extract_dh() const = 0;
virtual PKType::Type private_key_type() const = 0;
virtual size_t private_key_length() const = 0;
virtual void set_frame(const Frame::Ptr& frame_arg) = 0;
virtual void set_debug_level(const int debug_level) = 0;
virtual void set_flags(const unsigned int flags_arg) = 0;
virtual void set_ns_cert_type(const NSCert::Type ns_cert_type_arg) = 0;
virtual void set_remote_cert_tls(const KUParse::TLSWebType wt) = 0;
virtual void set_tls_remote(const std::string& tls_remote_arg) = 0;
virtual void set_tls_version_min(const TLSVersion::Type tvm) = 0;
virtual void set_tls_version_min_override(const std::string& override) = 0;
virtual void set_tls_cert_profile(const TLSCertProfile::Type type) = 0;
virtual void set_tls_cert_profile_override(const std::string& override) = 0;
virtual void set_local_cert_enabled(const bool v) = 0;
virtual void set_force_aes_cbc_ciphersuites(const bool v) = 0;
virtual void set_x509_track(X509Track::ConfigSet x509_track_config_arg) = 0;
virtual void set_rng(const RandomAPI::Ptr& rng_arg) = 0;
virtual void load(const OptionList& opt, const unsigned int lflags) = 0;
#ifdef OPENVPN_JSON_INTERNAL
virtual SSLConfigAPI::Ptr json_override(const Json::Value& root, const bool load_cert_key) const = 0;
#endif
virtual std::string validate_cert(const std::string& cert_txt) const = 0;
virtual std::string validate_cert_list(const std::string& certs_txt) const = 0;
virtual std::string validate_crl(const std::string& crl_txt) const = 0;
virtual std::string validate_private_key(const std::string& key_txt) const = 0;
virtual std::string validate_dh(const std::string& dh_txt) const = 0;
virtual SSLFactoryAPI::Ptr new_factory() = 0;
};
/**
* Reports a human readable string of the SSL library in use and its version.
* E.g. mbed TLS 1.2.4
*
* @return a human readable SSL library version string
*/
inline const std::string get_ssl_library_version();
}
#endif
@@ -0,0 +1,98 @@
// 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_SSL_SSLCHOOSE_H
#define OPENVPN_SSL_SSLCHOOSE_H
#ifdef USE_OPENSSL
#include <openvpn/openssl/crypto/api.hpp>
#include <openvpn/openssl/ssl/sslctx.hpp>
#include <openvpn/openssl/util/rand.hpp>
#include <openvpn/openssl/util/pem.hpp>
#endif
#ifdef USE_APPLE_SSL
#include <openvpn/applecrypto/crypto/api.hpp>
#include <openvpn/applecrypto/ssl/sslctx.hpp>
#include <openvpn/applecrypto/util/rand.hpp>
#endif
#ifdef USE_MBEDTLS
#include <mbedtls/platform.h>
#include <mbedtls/debug.h> // for debug_set_threshold
#include <openvpn/mbedtls/crypto/api.hpp>
#include <openvpn/mbedtls/ssl/sslctx.hpp>
#include <openvpn/mbedtls/util/rand.hpp>
#if defined(HAVE_OPENVPN_COMMON) && !defined(MBEDTLS_DISABLE_NAME_CONSTRAINTS)
#include <openvpn/mbedtls/ssl/sslctxnc.hpp>
#endif
#ifdef OPENVPN_PLATFORM_UWP
#include <openvpn/mbedtls/util/uwprand.hpp>
#endif
#include <openvpn/mbedtls/util/pem.hpp>
#endif
#ifdef USE_MBEDTLS_APPLE_HYBRID
#include <openvpn/applecrypto/crypto/api.hpp>
#include <openvpn/mbedtls/ssl/sslctx.hpp>
#include <openvpn/mbedtls/util/rand.hpp>
#endif
namespace openvpn {
namespace SSLLib {
#if defined(USE_MBEDTLS)
#define SSL_LIB_NAME "MbedTLS"
typedef MbedTLSCryptoAPI CryptoAPI;
#if defined(HAVE_OPENVPN_COMMON) && !defined(MBEDTLS_DISABLE_NAME_CONSTRAINTS)
typedef MbedTLSContextNameConstraints SSLAPI;
#else
typedef MbedTLSContext SSLAPI;
#endif
#if defined OPENVPN_PLATFORM_UWP
typedef MbedTLSRandomWithUWPEntropy RandomAPI;
#else
typedef MbedTLSRandom RandomAPI;
#endif
typedef MbedTLSPEM PEMAPI;
#elif defined(USE_MBEDTLS_APPLE_HYBRID)
// Uses Apple framework for CryptoAPI and MbedTLS for SSLAPI and RandomAPI
#define SSL_LIB_NAME "MbedTLSAppleHybrid"
typedef AppleCryptoAPI CryptoAPI;
typedef MbedTLSContext SSLAPI;
typedef MbedTLSRandom RandomAPI;
#elif defined(USE_APPLE_SSL)
#define SSL_LIB_NAME "AppleSSL"
typedef AppleCryptoAPI CryptoAPI;
typedef AppleSSLContext SSLAPI;
typedef AppleRandom RandomAPI;
#elif defined(USE_OPENSSL)
#define SSL_LIB_NAME "OpenSSL"
typedef OpenSSLCryptoAPI CryptoAPI;
typedef OpenSSLContext SSLAPI;
typedef OpenSSLRandom RandomAPI;
typedef OpenSSLPEM PEMAPI;
#else
#error no SSL library defined
#endif
}
}
#endif
@@ -0,0 +1,84 @@
// 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_SSL_SSLCONSTS_H
#define OPENVPN_SSL_SSLCONSTS_H
namespace openvpn {
namespace SSLConst {
// Special return values from SSL read/write methods
enum {
// Indicates that no cleartext data is available now (until
// more ciphertext is pushed into the SSL engine).
SHOULD_RETRY = -1,
// Return value from read_cleartext indicating that peer
// has sent a Close Notify message.
PEER_CLOSE_NOTIFY = -2,
};
// SSL config flags
enum {
// Show SSL status and cert chain in verify method
LOG_VERIFY_STATUS=(1<<0),
// Disable peer verification
NO_VERIFY_PEER=(1<<1),
// [client only] Enable client-side SNI (Server Name Indication)
// when hostname is provided
ENABLE_CLIENT_SNI=(1<<2),
// [client only] Don't require that the hostname matches
// the common name in the certificate.
NO_VERIFY_HOSTNAME=(1<<3),
// [server only] Don't automatically fail connections on
// bad peer cert. Succeed the connection, but pass the
// fail status data via AuthCert so the higher layers
// can handle it.
DEFERRED_CERT_VERIFY=(1<<4),
// [server only] When running as a server, require that
// clients that connect to us have their certificate
// purpose set to server.
SERVER_TO_SERVER=(1<<5),
// Peer certificate is optional
PEER_CERT_OPTIONAL=(1<<6),
// [server only] Send a list of client CAs to the client
SEND_CLIENT_CA_LIST=(1<<7),
// last flag marker
LAST=(1<<8)
};
// filter all but SSL flags
inline unsigned int ssl_flags(const unsigned int flags)
{
return flags & (LAST-1);
}
}
}
#endif
+29
View File
@@ -0,0 +1,29 @@
// 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_SSL_SSLLOG_H
#define OPENVPN_SSL_SSLLOG_H
#ifndef OPENVPN_LOG_SSL
#define OPENVPN_LOG_SSL(x) OPENVPN_LOG(x)
#endif
#endif
@@ -0,0 +1,145 @@
// 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/>.
// Parse the tls-cert-profile option.
#ifndef OPENVPN_SSL_TLS_CERT_PROFILE_H
#define OPENVPN_SSL_TLS_CERT_PROFILE_H
#include <string>
#include <openvpn/common/size.hpp>
#include <openvpn/common/exception.hpp>
#include <openvpn/common/options.hpp>
namespace openvpn {
namespace TLSCertProfile {
enum Type {
UNDEF=0,
#ifdef OPENVPN_USE_TLS_MD5
INSECURE,
#endif
LEGACY,
PREFERRED,
SUITEB,
};
inline Type default_if_undef(const Type type)
{
if (type == UNDEF)
return LEGACY; // this is the default if unspecified
else
return type;
}
inline const std::string to_string(const Type type)
{
switch (type)
{
case UNDEF:
return "UNDEF";
#ifdef OPENVPN_USE_TLS_MD5
case INSECURE:
return "INSECURE";
#endif
case LEGACY:
return "LEGACY";
case PREFERRED:
return "PREFERRED";
case SUITEB:
return "SUITEB";
default:
return "???";
}
}
inline Type parse_tls_cert_profile(const std::string& profile_name)
{
#ifdef OPENVPN_USE_TLS_MD5
if (profile_name == "insecure")
return INSECURE;
else
#endif
if (profile_name == "legacy")
return LEGACY;
else if (profile_name == "preferred")
return PREFERRED;
else if (profile_name == "suiteb")
return SUITEB;
else
throw option_error("tls-cert-profile: unrecognized profile name");
}
inline Type parse_tls_cert_profile(const OptionList& opt,
const std::string& relay_prefix)
{
const Option* o = opt.get_ptr(relay_prefix + "tls-cert-profile");
if (o)
{
const std::string profile_name = o->get_optional(1, 16);
return parse_tls_cert_profile(profile_name);
}
return UNDEF;
}
// If the override ends with "default", it is only applied
// if the config doesn't specify tls-cert-profile.
// Otherwise, the override has priority over the config.
inline void apply_override(Type& type, const std::string& override)
{
const Type orig = type;
if (override.empty() || override == "default")
;
#ifdef OPENVPN_USE_TLS_MD5
else if (override == "insecure-default")
{
if (orig == UNDEF)
type = INSECURE;
}
#endif
else if (override == "legacy-default")
{
if (orig == UNDEF)
type = LEGACY;
}
else if (override == "preferred-default")
{
if (orig == UNDEF)
type = PREFERRED;
}
#ifdef OPENVPN_USE_TLS_MD5
else if (override == "insecure")
type = INSECURE;
#endif
else if (override == "legacy")
type = LEGACY;
else if (override == "preferred")
type = PREFERRED;
else if (override == "suiteb")
type = SUITEB;
else
throw option_error("tls-cert-profile: unrecognized override string");
//OPENVPN_LOG("*** tls-cert-profile before=" << to_string(orig) << " override=" << override << " after=" << to_string(type));
}
}
}
#endif
@@ -0,0 +1,96 @@
// 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/>.
// test certificate subject and common name against tls_remote parameter
#ifndef OPENVPN_SSL_TLS_REMOTE_H
#define OPENVPN_SSL_TLS_REMOTE_H
#include <cstring>
#include <string>
#include <openvpn/common/string.hpp>
namespace openvpn {
namespace TLSRemote {
inline bool test(const std::string& tls_remote, const std::string& subject, const std::string& common_name)
{
return tls_remote == subject || string::starts_with(common_name, tls_remote);
}
inline void log(const std::string& tls_remote, const std::string& subject, const std::string& common_name)
{
OPENVPN_LOG("tls-remote validation" << std::endl << " tls-remote: '" << tls_remote << '\'' << std::endl << " Subj: '" << subject << '\'' << std::endl << " CN: '" << common_name << '\'');
}
// modifies x509 name in a way that is compatible with
// name remapping behavior on OpenVPN 2.x
inline std::string sanitize_x509_name(const std::string& str)
{
std::string ret;
bool leading_dash = true;
ret.reserve(str.length());
for (size_t i = 0; i < str.length(); ++i)
{
const char c = str[i];
if (c == '-' && leading_dash)
{
ret += '_';
continue;
}
leading_dash = false;
if ((c >= 'a' && c <= 'z')
|| (c >= 'A' && c <= 'Z')
|| (c >= '0' && c <= '9')
|| c == '_' || c == '-' || c == '.'
|| c == '@' || c == ':' || c == '/'
|| c == '=')
ret += c;
else
ret += '_';
}
return ret;
}
// modifies common name in a way that is compatible with
// name remapping behavior on OpenVPN 2.x
inline std::string sanitize_common_name(const std::string& str)
{
std::string ret;
ret.reserve(str.length());
for (size_t i = 0; i < str.length(); ++i)
{
const char c = str[i];
if ((c >= 'a' && c <= 'z')
|| (c >= 'A' && c <= 'Z')
|| (c >= '0' && c <= '9')
|| c == '_' || c == '-' || c == '.'
|| c == '@' || c == '/')
ret += c;
else
ret += '_';
}
return ret;
}
}
}
#endif
+395
View File
@@ -0,0 +1,395 @@
// 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/>.
// Implement the TLS-PRF function, used by ProtoContext.
#ifndef OPENVPN_SSL_TLSPRF_H
#define OPENVPN_SSL_TLSPRF_H
#include <cstring> // for std::strlen and others
#include <string>
#include <sstream>
#include <openvpn/common/size.hpp>
#include <openvpn/common/exception.hpp>
#include <openvpn/common/rc.hpp>
#include <openvpn/common/hexstr.hpp>
#include <openvpn/buffer/buffer.hpp>
#include <openvpn/buffer/bufcomplete.hpp>
#include <openvpn/crypto/static_key.hpp>
#include <openvpn/crypto/cryptoalgs.hpp>
#include <openvpn/ssl/psid.hpp>
#include <openvpn/random/randapi.hpp>
namespace openvpn {
template <typename CRYPTO_API>
class TLSPRF
{
public:
OPENVPN_SIMPLE_EXCEPTION(tlsprf_uninitialized);
OPENVPN_SIMPLE_EXCEPTION(tlsprf_client_server_mismatch);
TLSPRF(const bool server)
: initialized_(false), server_(server) {}
void randomize(RandomAPI& rng)
{
rng.assert_crypto();
if (!server_)
rng.rand_bytes(pre_master, sizeof(pre_master));
rng.rand_bytes(random1, sizeof(random1));
rng.rand_bytes(random2, sizeof(random2));
initialized_ = true;
}
void read(Buffer& buf)
{
if (!server_)
buf.read(pre_master, sizeof(pre_master));
buf.read(random1, sizeof(random1));
buf.read(random2, sizeof(random2));
initialized_ = true;
}
bool read_complete(BufferComplete& bc)
{
size_t need = sizeof(random1) + sizeof(random2);
if (!server_)
need += sizeof(pre_master);
if (!bc.advance(need))
return false;
return true;
}
void write(Buffer& buf)
{
verify_initialized();
if (!server_)
buf.write(pre_master, sizeof(pre_master));
buf.write(random1, sizeof(random1));
buf.write(random2, sizeof(random2));
}
void generate_key_expansion(OpenVPNStaticKey& dest, const TLSPRF& peer,
const ProtoSessionID& psid_self, const ProtoSessionID& psid_peer) const
{
if (server_ == peer.server_)
throw tlsprf_client_server_mismatch();
if (server_)
gen_exp(dest, peer, psid_peer, *this, psid_self);
else
gen_exp(dest, *this, psid_self, peer, psid_peer);
}
void erase()
{
if (initialized_)
{
if (!server_)
std::memset(pre_master, 0, sizeof(pre_master));
std::memset(random1, 0, sizeof(random1));
std::memset(random2, 0, sizeof(random2));
initialized_ = false;
}
}
std::string dump(const char *title)
{
std::ostringstream out;
out << "*** TLSPRF " << title << " pre_master: " << render_hex(pre_master, sizeof(pre_master)) << std::endl;
out << "*** TLSPRF " << title << " random1: " << render_hex(random1, sizeof(random1)) << std::endl;
out << "*** TLSPRF " << title << " random2: " << render_hex(random2, sizeof(random2)) << std::endl;
return out.str();
}
~TLSPRF()
{
erase();
}
static void openvpn_PRF (const unsigned char *secret,
const size_t secret_len,
const char *label,
const unsigned char *client_seed,
const size_t client_seed_len,
const unsigned char *server_seed,
const size_t server_seed_len,
const ProtoSessionID* client_sid,
const ProtoSessionID* server_sid,
unsigned char *output,
const size_t output_len)
{
const size_t label_len = std::strlen(label);
BufferAllocated seed(label_len
+ client_seed_len
+ server_seed_len
+ ProtoSessionID::SIZE * 2,
BufferAllocated::DESTRUCT_ZERO);
seed.write((unsigned char *)label, label_len);
seed.write(client_seed, client_seed_len);
seed.write(server_seed, server_seed_len);
if (client_sid)
client_sid->write(seed);
if (server_sid)
server_sid->write(seed);
// compute PRF
PRF(seed.data(), seed.size(), secret, secret_len, output, output_len);
}
private:
/*
* Use the TLS PRF function for generating data channel keys.
* This code is adapted from the OpenSSL library.
*
* TLS generates keys as such:
*
* master_secret[48] = PRF(pre_master_secret[48], "master secret",
* ClientHello.random[32] + ServerHello.random[32])
*
* key_block[] = PRF(SecurityParameters.master_secret[48],
* "key expansion",
* SecurityParameters.server_random[32] +
* SecurityParameters.client_random[32]);
*
* Notes:
*
* (1) key_block contains a full set of 4 keys.
* (2) The pre-master secret is generated by the client.
*/
static void hash (const CryptoAlgs::Type md,
const unsigned char *sec,
const size_t sec_len,
const unsigned char *seed,
const size_t seed_len,
unsigned char *out,
size_t olen)
{
size_t j;
unsigned char A1[CRYPTO_API::HMACContext::MAX_HMAC_SIZE];
size_t A1_len;
typename CRYPTO_API::HMACContext ctx;
typename CRYPTO_API::HMACContext ctx_tmp;
const size_t chunk = CryptoAlgs::size(md);
ctx.init(md, sec, sec_len);
ctx_tmp.init(md, sec, sec_len);
ctx.update(seed, seed_len);
A1_len = ctx.final(A1);
for (;;)
{
ctx.reset();
ctx_tmp.reset();
ctx.update(A1, A1_len);
ctx_tmp.update(A1, A1_len);
ctx.update(seed, seed_len);
if (olen > chunk)
{
j = ctx.final(out);
out+=j;
olen-=j;
A1_len = ctx_tmp.final(A1); /* calc the next A1 value */
}
else /* last one */
{
A1_len = ctx.final(A1);
memcpy(out,A1,olen);
break;
}
}
std::memset(A1, 0, sizeof(A1));
}
static void PRF (unsigned char *label,
const size_t label_len,
const unsigned char *sec,
const size_t slen,
unsigned char *out1,
const size_t olen)
{
size_t len, i;
const unsigned char *S1, *S2;
unsigned char *out2;
out2 = new unsigned char[olen];
len = slen / 2;
S1 = sec;
S2 = &(sec[len]);
len += (slen & 1); /* add for odd, make longer */
hash(CryptoAlgs::MD5,S1,len,label,label_len,out1,olen);
hash(CryptoAlgs::SHA1,S2,len,label,label_len,out2,olen);
for (i=0; i<olen; i++)
out1[i]^=out2[i];
std::memset(out2, 0, olen);
delete [] out2;
}
static void gen_exp(OpenVPNStaticKey& dest, const TLSPRF& client, const ProtoSessionID& psid_client,
const TLSPRF& server, const ProtoSessionID& psid_server)
{
static const char master_secret_id[] = "OpenVPN master secret";
static const char key_expansion_id[] = "OpenVPN key expansion";
unsigned char master[48];
client.verify_initialized();
server.verify_initialized();
// compute master secret
openvpn_PRF (client.pre_master,
sizeof(client.pre_master),
master_secret_id,
client.random1,
sizeof(client.random1),
server.random1,
sizeof(server.random1),
nullptr,
nullptr,
master,
sizeof(master));
// compute key expansion */
openvpn_PRF (master,
sizeof(master),
key_expansion_id,
client.random2,
sizeof(client.random2),
server.random2,
sizeof(server.random2),
&psid_client,
&psid_server,
dest.raw_alloc(),
OpenVPNStaticKey::KEY_SIZE);
std::memset(master, 0, sizeof(master));
}
void verify_initialized() const
{
if (!initialized_)
throw tlsprf_uninitialized();
}
bool initialized_;
bool server_;
unsigned char pre_master[48]; // client generated
unsigned char random1[32]; // generated by both client and server
unsigned char random2[32]; // generated by both client and server
};
// TLSPRF wrapper API using dynamic polymorphism
class TLSPRFInstance : public RC<thread_unsafe_refcount>
{
public:
typedef RCPtr<TLSPRFInstance> Ptr;
virtual void self_randomize(RandomAPI& rng) = 0;
virtual void self_write(Buffer& buf) = 0;
virtual void peer_read(Buffer& buf) = 0;
virtual bool peer_read_complete(BufferComplete& bc) = 0;
virtual void erase() = 0;
virtual void generate_key_expansion(OpenVPNStaticKey& dest,
const ProtoSessionID& psid_self,
const ProtoSessionID& psid_peer) const = 0;
};
class TLSPRFFactory : public RC<thread_unsafe_refcount>
{
public:
typedef RCPtr<TLSPRFFactory> Ptr;
virtual TLSPRFInstance::Ptr new_obj(const bool self_is_server) = 0;
};
// TLSPRF wrapper implementation using dynamic polymorphism
template <typename CRYPTO_API>
class CryptoTLSPRFInstance : public TLSPRFInstance
{
public:
CryptoTLSPRFInstance(const bool self_is_server)
: self(self_is_server),
peer(!self_is_server)
{
}
virtual void self_randomize(RandomAPI& rng)
{
self.randomize(rng);
}
virtual void self_write(Buffer& buf)
{
self.write(buf);
}
virtual void peer_read(Buffer& buf)
{
peer.read(buf);
}
virtual bool peer_read_complete(BufferComplete& bc)
{
return peer.read_complete(bc);
}
virtual void erase()
{
self.erase();
peer.erase();
}
virtual void generate_key_expansion(OpenVPNStaticKey& dest,
const ProtoSessionID& psid_self,
const ProtoSessionID& psid_peer) const
{
self.generate_key_expansion(dest, peer, psid_self, psid_peer);
}
private:
TLSPRF<CRYPTO_API> self;
TLSPRF<CRYPTO_API> peer;
};
template <typename CRYPTO_API>
class CryptoTLSPRFFactory : public TLSPRFFactory
{
public:
virtual TLSPRFInstance::Ptr new_obj(const bool self_is_server)
{
return new CryptoTLSPRFInstance<CRYPTO_API>(self_is_server);
}
};
} // namespace openvpn
#endif // OPENVPN_SSL_TLSPRF_H
+117
View File
@@ -0,0 +1,117 @@
// 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/>.
// Parse the tls-version-min option.
#ifndef OPENVPN_SSL_TLSVER_H
#define OPENVPN_SSL_TLSVER_H
#include <string>
#include <openvpn/common/size.hpp>
#include <openvpn/common/exception.hpp>
#include <openvpn/common/options.hpp>
namespace openvpn {
namespace TLSVersion {
enum Type {
UNDEF=0,
V1_0,
V1_1,
V1_2,
V1_3
};
inline const std::string to_string(const Type version)
{
switch (version)
{
case UNDEF:
return "UNDEF";
case V1_0:
return "V1_0";
case V1_1:
return "V1_1";
case V1_2:
return "V1_2";
case V1_3:
return "V1_3";
default:
return "???";
}
}
inline Type parse_tls_version_min(const std::string& ver,
const bool or_highest,
const Type max_version)
{
if (ver == "1.0" && V1_0 <= max_version)
return V1_0;
else if (ver == "1.1" && V1_1 <= max_version)
return V1_1;
else if (ver == "1.2" && V1_2 <= max_version)
return V1_2;
else if (ver == "1.3" && V1_3 <= max_version)
return V1_2;
else if (or_highest)
return max_version;
else
throw option_error("tls-version-min: unrecognized TLS version");
}
inline Type parse_tls_version_min(const OptionList& opt,
const std::string& relay_prefix,
const Type max_version)
{
const Option* o = opt.get_ptr(relay_prefix + "tls-version-min");
if (o)
{
const std::string ver = o->get_optional(1, 16);
const bool or_highest = (o->get_optional(2, 16) == "or-highest");
return parse_tls_version_min(ver, or_highest, max_version);
}
return UNDEF;
}
inline void apply_override(Type& tvm, const std::string& override)
{
//const Type orig = tvm;
if (override.empty() || override == "default")
;
else if (override == "disabled")
tvm = UNDEF;
else if (override == "tls_1_0")
tvm = V1_0;
else if (override == "tls_1_1")
tvm = V1_1;
else if (override == "tls_1_2")
tvm = V1_2;
else if (override == "tls_1_3")
tvm = V1_3;
else
throw option_error("tls-version-min: unrecognized override string");
//OPENVPN_LOG("*** TLS-version-min before=" << to_string(orig) << " override=" << override << " after=" << to_string(tvm)); // fixme
}
}
}
#endif