mirror of
https://github.com/deneraraujo/OpenVPNAdapter.git
synced 2026-04-24 00:00:05 +08:00
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:
+361
-41
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user