Squashed 'Sources/OpenVPNAdapter/Libraries/Vendors/openvpn/' changes from cc90cde57..6608878d5

6608878d5 [OVPN3-341] implement mssfix support
1bf3fc0e4 win: update project files
f8d209435 travis: update to default osx image: xcode9.4
31eb246a8 travis.yml: align deps version to lib-version
996f86635 RunContext: fixed rebase issue that added two "default: signal_rearm();" clauses
aebea6456 build script: minor changes to Cityhash inclusion
1d754072c modstat: make update_file_mod_time_nanoseconds() a no-op on non-Linux
7974c9867 Fixed some breakage caused by recent endian/ffs commits
a0dd7fe8b endian.hpp: break out endian compile-time tests to endian_platform.hpp
c8bdf5a34 ffs.hpp: support additional numeric types
dcb0c9452 BufferType: append() argument can now be a flexible buffer type
2009a8a25 Added AsioTimerSafe
39e71b7dd event_loop_wait_barrier: use a longer default timeout when running under valgrind
8b7e08e9b string::contains_non_space_ctrl: consider ASCII char 127 (DEL) to be a control char
e43024d7c RunContext: rearm non-terminating signals
6ab379323 write_binary_atomic: remove temporary file on move failure
55dc653cd path: added is_contained()
02bf235c6 Reverted previous commit: "ReplyParser: added undefined status"
84dbc5b9b Allow test/cli.cpp to be used with NetCfg Tunbuilder client
80fed2c55 Allow updating auth-token during session
ad7da751e don't print time in debug message and use OPENVPN_LOG_PROTO_VERBOSE
981407994 tls-crypt-v2: implement abstract metadata parser
be38bbeb8 tls-crypt-v2: test/ssl/proto.cpp - extend protocol test
60fcf374f tls-crypt-v2: implement WKc appending/unwrapping logic
51f4a3a29 tls-crypt-v2: introduce CONTROL_HARD_RESET_V3 packet type
156a6e58b tls-crypt-v2: implement client key parser and renderer
54a97b381 ssl: add support for encoding/decoding PEM format
f090fcda4 tls-crypt: make HMAC API more generic
d87f5bbc0 OpenSSL: init library
2ea88a93b Add Remote endpoint information to protect_socket call
0a081ee17 [OVPN3-315] cli/go: add option to compile SITNL component
5bbfb57c0 [OVPN3-315] TunLinux::Client: allow user to select netlink at compile time
e8458a68e [OVPN3-315] GW: add netlink support
4e77edb9e [OVPN3-315] TunLinux: add Netlink implementation for Tun setup methods
68508fe56 bigmutex: include missing extern.hpp header
a7b923e1e Fix logic inversion from commit 2de9aebc
923e10d13 runcontext: arrange members to allow inheritance
2de9aebc7 Replace deprecated mbedtls_sha1 with mbedtls_sha1_ret
e9c0bd00b Remove unused private field
ee17c33c2 Add virtual deconstructor to TransportClientParent
fab64ba0f Fix clang warning about unused attributes and missing overrides
2624d9ddf Also parse dhcp-option DNS6 as DNS server for compatibility with OpenVPN 2
6d12c9cc2 Refuse external pki with non RSA keys
4a25059f5 test/ovpncli: Don't override PROF env variable
f241c4c5f scripts: Add tool to update copyright years
27beeb03d Update lz4 version to 1.8.3
17e356858 Define DASIO_HAS_STD_STRING_VIEW on Android build
b107fd994 Remove unsupported platforms from Android build
6a200f72e Ensure all Android components are always installed
fbcd374a4 [OVPN3-327] OpenSSL: ensure >TLS1.0 is negotiated by default
d9b1f78b6 JSON: #define OPENVPN_JSON_INTERNAL when internal JSON library is used
39290f19d Fix build issues with #if macro on big-endian hardware
d4f62d9ed Fix instantiating a new URL instead of parsing the URL

git-subtree-dir: Sources/OpenVPNAdapter/Libraries/Vendors/openvpn
git-subtree-split: 6608878d57eec1c64c16c5a13ee65b2cf0418ca1
This commit is contained in:
Sergey Abramchuk
2019-01-13 13:08:42 +03:00
parent a01ecd6c88
commit f5fda0fa73
72 changed files with 3685 additions and 490 deletions
+361 -41
View File
@@ -48,6 +48,10 @@
#include <openvpn/buffer/buffer.hpp>
#include <openvpn/buffer/safestr.hpp>
#include <openvpn/buffer/bufcomposed.hpp>
#include <openvpn/ip/ip4.hpp>
#include <openvpn/ip/ip6.hpp>
#include <openvpn/ip/udp.hpp>
#include <openvpn/ip/tcp.hpp>
#include <openvpn/time/time.hpp>
#include <openvpn/time/durhelper.hpp>
#include <openvpn/frame/frame.hpp>
@@ -57,6 +61,7 @@
#include <openvpn/crypto/cipher.hpp>
#include <openvpn/crypto/ovpnhmac.hpp>
#include <openvpn/crypto/tls_crypt.hpp>
#include <openvpn/crypto/tls_crypt_v2.hpp>
#include <openvpn/crypto/packet_id.hpp>
#include <openvpn/crypto/static_key.hpp>
#include <openvpn/crypto/bs64_data_limit.hpp>
@@ -65,6 +70,8 @@
#include <openvpn/ssl/psid.hpp>
#include <openvpn/ssl/tlsprf.hpp>
#include <openvpn/ssl/datalimit.hpp>
#include <openvpn/ssl/mssparms.hpp>
#include <openvpn/transport/mssfix.hpp>
#include <openvpn/transport/protocol.hpp>
#include <openvpn/tun/layer.hpp>
#include <openvpn/tun/tunmtu.hpp>
@@ -171,6 +178,7 @@ namespace openvpn {
// indicates key_method >= 2
CONTROL_HARD_RESET_CLIENT_V2 = 7, // initial key from client, forget previous state
CONTROL_HARD_RESET_CLIENT_V3 = 10, // initial key from client, forget previous state
CONTROL_HARD_RESET_SERVER_V2 = 8, // initial key from server, forget previous state
// define the range of legal opcodes
@@ -291,8 +299,10 @@ namespace openvpn {
// compressor
CompressContext comp_ctx;
// tls_auth parms
// tls_auth/crypt parms
OpenVPNStaticKey tls_key; // leave this undefined to disable tls_auth/crypt
bool tls_crypt_v2 = false; // needed to distinguish between tls-crypt and tls-crypt-v2 server mode
BufferAllocated wkc; // leave this undefined to disable tls-crypt-v2 on client
OvpnHMACFactory::Ptr tls_auth_factory;
OvpnHMACContext::Ptr tls_auth_context;
@@ -301,6 +311,8 @@ namespace openvpn {
TLSCryptFactory::Ptr tls_crypt_factory;
TLSCryptContext::Ptr tls_crypt_context;
TLSCryptMetadataFactory::Ptr tls_crypt_metadata_factory;
// reliability layer parms
reliable::id_t reliable_window = 0;
size_t max_ack_list = 0;
@@ -332,13 +344,15 @@ namespace openvpn {
// MTU
unsigned int tun_mtu = 1500;
MSSParms mss_parms;
unsigned int mss_inter = 0;
// Debugging
int debug_level = 1;
// Compatibility
bool force_aes_cbc_ciphersuites = false;
// For compatibility with openvpn2 we send initial options on rekeying,
// instead of possible modifications caused by NCP
std::string initial_options;
@@ -433,6 +447,8 @@ namespace openvpn {
{
if (tls_auth_context)
throw proto_option_error("tls-auth and tls-crypt are mutually exclusive");
if (tls_crypt_context)
throw proto_option_error("tls-crypt and tls-crypt-v2 are mutually exclusive");
tls_key.parse(o->get(1, 0));
@@ -445,6 +461,46 @@ namespace openvpn {
set_tls_crypt_algs(digest, cipher);
}
}
// tls-crypt-v2
{
const Option *o = opt.get_ptr(relay_prefix("tls-crypt-v2"));
if (o)
{
if (tls_auth_context)
throw proto_option_error("tls-auth and tls-crypt-v2 are mutually exclusive");
if (tls_crypt_context)
throw proto_option_error("tls-crypt and tls-crypt-v2 are mutually exclusive");
digest = CryptoAlgs::lookup("SHA256");
cipher = CryptoAlgs::lookup("AES-256-CTR");
if ((digest == CryptoAlgs::NONE) || (cipher == CryptoAlgs::NONE))
throw proto_option_error("missing support for tls-crypt-v2 algorithms");
// initialize tls_crypt_context
set_tls_crypt_algs(digest, cipher);
std::string keyfile = o->get(1, 0);
if (opt.exists("client"))
{
// in client mode expect the key to be a PEM encoded tls-crypt-v2 client key (key + WKc)
TLSCryptV2ClientKey tls_crypt_v2_key(tls_crypt_context);
tls_crypt_v2_key.parse(keyfile);
tls_crypt_v2_key.extract_key(tls_key);
tls_crypt_v2_key.extract_wkc(wkc);
}
else
{
// in server mode this is a PEM encoded tls-crypt-v2 server key
TLSCryptV2ServerKey tls_crypt_v2_key;
tls_crypt_v2_key.parse(keyfile);
tls_crypt_v2_key.extract_key(tls_key);
}
tls_crypt_v2 = true;
}
}
}
// key-direction
@@ -507,6 +563,9 @@ namespace openvpn {
// tun-mtu
tun_mtu = parse_tun_mtu(opt, tun_mtu);
// mssfix
mss_parms.parse(opt);
// load parameters that can be present in both config file or pushed options
load_common(opt, pco, server ? LOAD_COMMON_SERVER : LOAD_COMMON_CLIENT);
}
@@ -680,13 +739,18 @@ namespace openvpn {
return tls_key.defined() && tls_crypt_context;
}
bool tls_crypt_v2_enabled() const
{
return tls_crypt_enabled() && tls_crypt_v2;
}
// generate a string summarizing options that will be
// transmitted to peer for options consistency check
std::string options_string()
{
if (!initial_options.empty())
return initial_options;
std::ostringstream out;
const bool server = ssl_factory->mode().is_server();
@@ -726,7 +790,7 @@ namespace openvpn {
out << ",tls-server";
else
out << ",tls-client";
initial_options = out.str();
return initial_options;
@@ -892,6 +956,7 @@ namespace openvpn {
break;
}
case CONTROL_HARD_RESET_CLIENT_V2:
case CONTROL_HARD_RESET_CLIENT_V3:
{
if (!proto.is_server())
return;
@@ -946,6 +1011,8 @@ namespace openvpn {
return "DATA_V2";
case CONTROL_HARD_RESET_CLIENT_V2:
return "CONTROL_HARD_RESET_CLIENT_V2";
case CONTROL_HARD_RESET_CLIENT_V3:
return "CONTROL_HARD_RESET_CLIENT_V3";
case CONTROL_HARD_RESET_SERVER_V2:
return "CONTROL_HARD_RESET_SERVER_V2";
}
@@ -1217,6 +1284,8 @@ namespace openvpn {
public:
typedef RCPtr<KeyContext> Ptr;
OPENVPN_SIMPLE_EXCEPTION(tls_crypt_unwrap_wkc_error);
// KeyContext events occur on two basic key types:
// Primary Key -- the key we transmit/encrypt on.
// Secondary Key -- new keys and retiring keys.
@@ -1351,7 +1420,7 @@ namespace openvpn {
send_reset();
set_state(state+1);
dirty = true;
}
}
}
// control channel flush
@@ -1466,6 +1535,10 @@ namespace openvpn {
// decompress packet
if (compress)
compress->decompress(buf);
// set MSS for segments server can receive
if (proto.config->mss_inter > 0)
MSSFix::mssfix(buf, proto.config->mss_inter);
}
else
buf.reset_size(); // no crypto context available
@@ -1607,6 +1680,15 @@ namespace openvpn {
{
case TLS_AUTH:
return validate_tls_auth(recv, proto, now);
case TLS_CRYPT_V2:
if (opcode_extract(recv[0]) == CONTROL_HARD_RESET_CLIENT_V3)
{
// skip validation of HARD_RESET_V3 because the tls-crypt
// engine has not been initialized yet
OPENVPN_LOG_PROTO_VERBOSE("SKIPPING VALIDATION OF HARD_RESET_V3");
return true;
}
/* no break */
case TLS_CRYPT:
return validate_tls_crypt(recv, proto, now);
case TLS_PLAIN:
@@ -1675,6 +1757,35 @@ namespace openvpn {
// cache op32 for hot path in do_encrypt
cache_op32();
int crypto_encap = (enable_op32 ? OP_SIZE_V2 : 1) +
c.comp_ctx.extra_payload_bytes() +
PacketID::size(PacketID::SHORT_FORM) +
c.dc.context().encap_overhead();
int transport_encap = 0;
if (c.mss_parms.mtu)
{
if (proto.is_tcp())
transport_encap += sizeof(struct TCPHeader);
else
transport_encap += sizeof(struct UDPHeader);
if (c.protocol.is_ipv6())
transport_encap += sizeof(struct IPv6Header);
else
transport_encap += sizeof(struct IPv4Header);
transport_encap += c.protocol.extra_transport_bytes();
}
if (c.mss_parms.mssfix != 0)
{
OPENVPN_LOG_PROTO("MTU mssfix=" << c.mss_parms.mssfix <<
" crypto_encap=" << crypto_encap <<
" transport_encap=" << transport_encap);
c.mss_inter = c.mss_parms.mssfix - (crypto_encap + transport_encap);
}
}
}
@@ -1765,7 +1876,9 @@ namespace openvpn {
work.inc_size(decrypt_bytes);
// verify HMAC
if (!proto.tls_crypt_recv->hmac_cmp(orig_data, orig_size, work.c_data(), work.size()))
if (!proto.tls_crypt_recv->hmac_cmp(orig_data,
TLSCryptContext::hmac_offset,
work.c_data(), work.size()))
return false;
// verify source PSID
@@ -1819,6 +1932,10 @@ namespace openvpn {
{
bool pid_wrap;
// set MSS for segments client can receive
if (proto.config->mss_inter > 0)
MSSFix::mssfix(buf, proto.config->mss_inter);
// compress packet
if (compress)
compress->compress(buf, compress_hint);
@@ -1990,25 +2107,36 @@ namespace openvpn {
set_event(ev);
}
unsigned int initial_op(const bool sender) const
unsigned int initial_op(const bool sender, const bool tls_crypt_v2) const
{
if (key_id_)
return CONTROL_SOFT_RESET_V1;
{
return CONTROL_SOFT_RESET_V1;
}
else
return (proto.is_server() == sender) ? CONTROL_HARD_RESET_SERVER_V2 : CONTROL_HARD_RESET_CLIENT_V2;
{
if (proto.is_server() == sender)
return CONTROL_HARD_RESET_SERVER_V2;
if (!tls_crypt_v2)
return CONTROL_HARD_RESET_CLIENT_V2;
else
return CONTROL_HARD_RESET_CLIENT_V3;
}
}
void send_reset()
{
Packet pkt;
pkt.opcode = initial_op(true);
pkt.opcode = initial_op(true, proto.tls_wrap_mode == TLS_CRYPT_V2);
pkt.frame_prepare(*proto.config->frame, Frame::WRITE_SSL_INIT);
raw_send(std::move(pkt));
}
void raw_recv(Packet&& raw_pkt) // called by ProtoStackBase
{
if (raw_pkt.buf->empty() && raw_pkt.opcode == initial_op(false))
if (raw_pkt.buf->empty() &&
raw_pkt.opcode == initial_op(false, proto.tls_wrap_mode == TLS_CRYPT_V2))
{
switch (state)
{
@@ -2265,14 +2393,15 @@ namespace openvpn {
// write opcode
work.push_front(op_compose(opcode, key_id_));
// compute HMAC using header fields (from 'work') and plaintext payload (from 'buf')
proto.tls_crypt_send->hmac_gen(work.data(), work.size(), buf.c_data(), buf.size());
// compute HMAC using header fields (from 'work') and plaintext
// payload (from 'buf')
proto.tls_crypt_send->hmac_gen(work.data(), TLSCryptContext::hmac_offset,
buf.c_data(), buf.size());
const size_t head_size = 1 + ProtoSessionID::SIZE + PacketID::size(PacketID::LONG_FORM);
const size_t data_offset = head_size + proto.hmac_size;
const size_t data_offset = TLSCryptContext::hmac_offset + proto.hmac_size;
// encrypt the content of 'buf' (packet payload) into 'work'
const size_t decrypt_bytes = proto.tls_crypt_send->encrypt(work.c_data() + head_size,
const size_t decrypt_bytes = proto.tls_crypt_send->encrypt(work.c_data() + TLSCryptContext::hmac_offset,
work.data() + data_offset,
work.max_size() - data_offset,
buf.c_data(), buf.size());
@@ -2283,6 +2412,11 @@ namespace openvpn {
}
work.inc_size(decrypt_bytes);
// append WKc to wrapped packet for tls-crypt-v2
if ((opcode == CONTROL_HARD_RESET_CLIENT_V3)
&& (proto.tls_wrap_mode == TLS_CRYPT_V2))
proto.tls_crypt_append_wkc(work);
// 'work' now contains the complete packet ready to go. swap it with 'buf'
buf.swap(work);
}
@@ -2303,6 +2437,7 @@ namespace openvpn {
gen_head_tls_auth(opcode, buf);
break;
case TLS_CRYPT:
case TLS_CRYPT_V2:
gen_head_tls_crypt(opcode, buf);
break;
case TLS_PLAIN:
@@ -2448,15 +2583,14 @@ namespace openvpn {
// skip the hmac
recv.advance(proto.hmac_size);
const size_t head_size = 1 + ProtoSessionID::SIZE + PacketID::size(PacketID::LONG_FORM);
const size_t data_offset = head_size + proto.hmac_size;
const size_t data_offset = TLSCryptContext::hmac_offset + proto.hmac_size;
if (orig_size < data_offset)
return false;
// decrypt payload
proto.config->frame->prepare(Frame::DECRYPT_WORK, work);
const size_t decrypt_bytes = proto.tls_crypt_recv->decrypt(orig_data + head_size,
const size_t decrypt_bytes = proto.tls_crypt_recv->decrypt(orig_data + TLSCryptContext::hmac_offset,
work.data(), work.max_size(),
recv.c_data(), recv.size());
if (!decrypt_bytes)
@@ -2470,7 +2604,8 @@ namespace openvpn {
work.inc_size(decrypt_bytes);
// verify HMAC
if (!proto.tls_crypt_recv->hmac_cmp(orig_data, orig_size, work.c_data(), work.size()))
if (!proto.tls_crypt_recv->hmac_cmp(orig_data, TLSCryptContext::hmac_offset,
work.c_data(), work.size()))
{
proto.stats->error(Error::HMAC_ERROR);
if (proto.is_tcp())
@@ -2528,6 +2663,100 @@ namespace openvpn {
return false;
}
bool unwrap_tls_crypt_wkc(Buffer &recv)
{
// the ``WKc`` is located at the end of the packet, after the tls-crypt
// payload.
// Format is as follows (as documented by Steffan Krager):
//
// ``len = len(WKc)`` (16 bit, network byte order)
// ``T = HMAC-SHA256(Ka, len || Kc || metadata)``
// ``IV = 128 most significant bits of T``
// ``WKc = T || AES-256-CTR(Ke, IV, Kc || metadata) || len``
const unsigned char *orig_data = recv.data();
const size_t orig_size = recv.size();
const size_t hmac_size = proto.config->tls_crypt_context->digest_size();
const size_t tls_frame_size = 1 + ProtoSessionID::SIZE +
PacketID::size(PacketID::LONG_FORM) +
hmac_size +
// the following is the tls-crypt payload
sizeof(char) + // length of ACK array
sizeof(id_t); // reliable ID
// check that at least the authentication tag ``T`` is present
if (orig_size < (tls_frame_size + hmac_size))
return false;
// the ``WKc`` is just appended after the standard tls-crypt frame
const unsigned char *wkc_raw = orig_data + tls_frame_size;
const size_t wkc_raw_size = orig_size - tls_frame_size - sizeof(uint16_t);
// retrieve the ``WKc`` len from the bottom of the packet and convert it to Host Order
uint16_t wkc_len = ntohs(*(uint16_t *)(wkc_raw + wkc_raw_size));
// length sanity check (the size of the ``len`` field is included in the value)
if ((wkc_len - sizeof(uint16_t)) != wkc_raw_size)
return false;
BufferAllocated plaintext(wkc_len, BufferAllocated::CONSTRUCT_ZERO);
// plaintext will be used to compute the Auth Tag, therefore start by prepnding
// the WKc length in network order
wkc_len = htons(wkc_len);
plaintext.write(&wkc_len, sizeof(wkc_len));
const size_t decrypt_bytes = proto.tls_crypt_server->decrypt(wkc_raw,
plaintext.data() + 2,
plaintext.max_size() - 2,
wkc_raw + hmac_size,
wkc_raw_size - hmac_size);
plaintext.inc_size(decrypt_bytes);
// decrypted data must at least contain a full 2048bits client key
// (metadata is optional)
if (plaintext.size() < OpenVPNStaticKey::KEY_SIZE)
{
proto.stats->error(Error::DECRYPT_ERROR);
if (proto.is_tcp())
invalidate(Error::DECRYPT_ERROR);
return false;
}
if (!proto.tls_crypt_server->hmac_cmp(wkc_raw, 0,
plaintext.c_data(),
plaintext.size()))
{
proto.stats->error(Error::HMAC_ERROR);
if (proto.is_tcp())
invalidate(Error::HMAC_ERROR);
return false;
}
// we can now remove the WKc length from the plaintext, as it is not
// really part of the key material
plaintext.advance(sizeof(wkc_len));
// WKc has been authenticated: it contains the client key followed
// by the optional metadata. Let's initialize the tls-crypt context
// with the client key
OpenVPNStaticKey client_key;
plaintext.read(client_key.raw_alloc(), OpenVPNStaticKey::KEY_SIZE);
proto.reset_tls_crypt(*proto.config, client_key);
// verify metadata
int metadata_type = -1;
if (!plaintext.empty())
metadata_type = plaintext.pop_front();
if (!proto.tls_crypt_metadata->verify(metadata_type, plaintext))
{
proto.stats->error(Error::TLS_CRYPT_META_FAIL);
return false;
}
// virtually remove the WKc from the packet
recv.set_size(tls_frame_size);
return true;
}
bool decapsulate(Packet& pkt) // called by ProtoStackBase
{
try {
@@ -2535,6 +2764,20 @@ namespace openvpn {
{
case TLS_AUTH:
return decapsulate_tls_auth(pkt);
case TLS_CRYPT_V2:
if (pkt.opcode == CONTROL_HARD_RESET_CLIENT_V3)
{
// unwrap WKc and extract Kc (client key) from packet.
// This way we can initialize the tls-crypt per-client contexts
// (this happens on the server side only)
if (!unwrap_tls_crypt_wkc(*pkt.buf))
{
return false;
}
}
// now that the tls-crypt contexts have been initialized it is
// possible to proceed with the standard tls-crypt decapsulation
/* no break */
case TLS_CRYPT:
return decapsulate_tls_crypt(pkt);
case TLS_PLAIN:
@@ -2724,15 +2967,14 @@ namespace openvpn {
if (opcode_extract(op) != reset_op || key_id_extract(op) != 0)
return false;
const size_t head_size = 1 + ProtoSessionID::SIZE + PacketID::size(PacketID::LONG_FORM);
const size_t data_offset = head_size + tls_crypt_recv->output_hmac_size();
const size_t data_offset = TLSCryptContext::hmac_offset + tls_crypt_recv->output_hmac_size();
if (net_buf.size() < data_offset)
return false;
frame->prepare(Frame::DECRYPT_WORK, work);
// decrypt payload from 'net_buf' into 'work'
const size_t decrypt_bytes = tls_crypt_recv->decrypt(net_buf.c_data() + head_size,
const size_t decrypt_bytes = tls_crypt_recv->decrypt(net_buf.c_data() + TLSCryptContext::hmac_offset,
work.data(), work.max_size(),
net_buf.c_data() + data_offset,
net_buf.size() - data_offset);
@@ -2742,7 +2984,8 @@ namespace openvpn {
work.inc_size(decrypt_bytes);
// verify HMAC
return tls_crypt_recv->hmac_cmp(net_buf.c_data(), net_buf.size(),
return tls_crypt_recv->hmac_cmp(net_buf.c_data(),
TLSCryptContext::hmac_offset,
work.data(), work.size());
}
catch (BufferException&)
@@ -2751,11 +2994,30 @@ namespace openvpn {
return false;
}
protected:
unsigned int reset_op;
private:
TLSCryptInstance::Ptr tls_crypt_recv;
Frame::Ptr frame;
BufferAllocated work;
unsigned int reset_op;
};
class TLSCryptV2PreValidate : public TLSCryptPreValidate
{
public:
OPENVPN_SIMPLE_EXCEPTION(tls_crypt_v2_pre_validate);
TLSCryptV2PreValidate(const Config& c, const bool server)
: TLSCryptPreValidate(c, server)
{
if (!c.tls_crypt_v2_enabled())
throw tls_crypt_v2_pre_validate();
// in case of server peer, we expect the new v3 packet type
if (server)
reset_op = CONTROL_HARD_RESET_CLIENT_V3;
}
};
OPENVPN_SIMPLE_EXCEPTION(select_key_context_error);
@@ -2771,20 +3033,27 @@ namespace openvpn {
const Config& c = *config;
// tls-auth setup
if (c.tls_auth_context)
if (c.tls_crypt_v2_enabled())
{
tls_wrap_mode = TLS_AUTH;
tls_wrap_mode = TLS_CRYPT_V2;
// get HMAC size from Digest object
hmac_size = c.tls_auth_context->size();
hmac_size = c.tls_crypt_context->digest_size();
}
else if (c.tls_crypt_context)
else if (c.tls_crypt_enabled())
{
tls_wrap_mode = TLS_CRYPT;
// get HMAC size from Digest object
hmac_size = c.tls_crypt_context->digest_size();
}
else if (c.tls_auth_enabled())
{
tls_wrap_mode = TLS_AUTH;
// get HMAC size from Digest object
hmac_size = c.tls_auth_context->size();
}
else
{
tls_wrap_mode = TLS_PLAIN;
@@ -2801,6 +3070,43 @@ namespace openvpn {
return 0;
}
void reset_tls_crypt(const Config& c, const OpenVPNStaticKey& key)
{
tls_crypt_send = c.tls_crypt_context->new_obj_send();
tls_crypt_recv = c.tls_crypt_context->new_obj_recv();
// static direction assignment - not user configurable
unsigned int key_dir = is_server() ?
OpenVPNStaticKey::NORMAL :
OpenVPNStaticKey::INVERSE;
tls_crypt_send->init(key.slice(OpenVPNStaticKey::HMAC |
OpenVPNStaticKey::ENCRYPT | key_dir),
key.slice(OpenVPNStaticKey::CIPHER |
OpenVPNStaticKey::ENCRYPT | key_dir));
tls_crypt_recv->init(key.slice(OpenVPNStaticKey::HMAC |
OpenVPNStaticKey::DECRYPT | key_dir),
key.slice(OpenVPNStaticKey::CIPHER |
OpenVPNStaticKey::DECRYPT | key_dir));
}
void reset_tls_crypt_server(const Config& c)
{
//tls-crypt session key is derived later from WKc received from the client
tls_crypt_send.reset();
tls_crypt_recv.reset();
//server context is used only to process incoming WKc's
tls_crypt_server = c.tls_crypt_context->new_obj_recv();
//the server key is composed by one key set only, therefore direction and
//mode should not be specified when slicing
tls_crypt_server->init(c.tls_key.slice(OpenVPNStaticKey::HMAC),
c.tls_key.slice(OpenVPNStaticKey::CIPHER));
tls_crypt_metadata = c.tls_crypt_metadata_factory->new_obj();
}
void reset()
{
const Config& c = *config;
@@ -2820,16 +3126,19 @@ namespace openvpn {
switch (tls_wrap_mode)
{
case TLS_CRYPT:
tls_crypt_send = c.tls_crypt_context->new_obj_send();
tls_crypt_recv = c.tls_crypt_context->new_obj_recv();
// static direction assignment - not user configurable
key_dir = is_server() ? OpenVPNStaticKey::NORMAL : OpenVPNStaticKey::INVERSE;
tls_crypt_send->init(c.tls_key.slice(OpenVPNStaticKey::HMAC | OpenVPNStaticKey::ENCRYPT | key_dir),
c.tls_key.slice(OpenVPNStaticKey::CIPHER | OpenVPNStaticKey::ENCRYPT | key_dir));
tls_crypt_recv->init(c.tls_key.slice(OpenVPNStaticKey::HMAC | OpenVPNStaticKey::DECRYPT | key_dir),
c.tls_key.slice(OpenVPNStaticKey::CIPHER | OpenVPNStaticKey::DECRYPT | key_dir));
reset_tls_crypt(c, c.tls_key);
// init tls_crypt packet ID
ta_pid_send.init(PacketID::LONG_FORM);
ta_pid_recv.init(c.pid_mode, PacketID::LONG_FORM, "SSL-CC", 0, stats);
break;
case TLS_CRYPT_V2:
if (is_server())
// setup key to be used to unwrap WKc upon client connection.
// tls-crypt session key setup is postponed to reception of WKc
// from client
reset_tls_crypt_server(c);
else
reset_tls_crypt(c, c.tls_key);
// init tls_crypt packet ID
ta_pid_send.init(PacketID::LONG_FORM);
ta_pid_recv.init(c.pid_mode, PacketID::LONG_FORM, "SSL-CC", 0, stats);
@@ -2860,7 +3169,7 @@ namespace openvpn {
break;
case TLS_PLAIN:
break;
}
}
// initialize proto session ID
psid_self.randomize(*c.prng);
@@ -3199,7 +3508,8 @@ namespace openvpn {
enum TLSWrapMode {
TLS_PLAIN,
TLS_AUTH,
TLS_CRYPT
TLS_CRYPT,
TLS_CRYPT_V2
};
void reset_all()
@@ -3483,6 +3793,13 @@ namespace openvpn {
keepalive_xmit = kx;
}
void tls_crypt_append_wkc(BufferAllocated& dst)
{
if (!config->wkc.defined())
throw proto_error("Client Key Wrapper undefined");
dst.append(config->wkc);
}
// BEGIN ProtoContext data members
Config::Ptr config;
@@ -3506,6 +3823,9 @@ namespace openvpn {
TLSCryptInstance::Ptr tls_crypt_send;
TLSCryptInstance::Ptr tls_crypt_recv;
TLSCryptInstance::Ptr tls_crypt_server;
TLSCryptMetadata::Ptr tls_crypt_metadata;
PacketIDSend ta_pid_send;
PacketIDReceive ta_pid_recv;