mirror of
https://github.com/deneraraujo/OpenVPNAdapter.git
synced 2026-04-24 00:00:05 +08:00
Merge commit '86cc97e55fe346502462284d2e636a2b3708163e' as 'Sources/OpenVPN3'
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user