0
0
mirror of https://github.com/OpenVPN/openvpn3.git synced 2024-09-20 12:12:15 +02:00

SSL layer: added RFC 5077 TLS session resumption ticket support

This is an initial client and server-side implementation
for OpenSSL 1.0.2.

Note that this functionality is intended for use with
HTTP sessions, and should not be used with the OpenVPN
protocol.

Signed-off-by: James Yonan <james@openvpn.net>
This commit is contained in:
James Yonan 2019-03-15 23:18:19 -06:00 committed by David Sommerseth
parent e0a821ddd6
commit 162eeaa485
No known key found for this signature in database
GPG Key ID: 86CF944C9671FDF2
5 changed files with 620 additions and 17 deletions

View File

@ -236,6 +236,18 @@ namespace openvpn {
external_pki = external_pki_arg;
}
virtual void set_session_ticket_handler(TLSSessionTicketBase* session_ticket_handler_arg)
{
// fixme -- this method should be implemented for server-side TLS session resumption tickets
throw MbedTLSException("set_session_ticket_handler not implemented");
}
virtual void set_client_session_tickets(const bool v)
{
// fixme -- this method should be implemented for client-side TLS session resumption tickets
throw MbedTLSException("set_client_session_tickets not implemented");
}
virtual void set_private_key_password(const std::string& pwd)
{
priv_key_pwd = pwd;
@ -692,11 +704,16 @@ namespace openvpn {
return "";
}
virtual const AuthCert::Ptr& auth_cert() const
virtual const AuthCert::Ptr& auth_cert()
{
return authcert;
}
virtual void mark_no_cache()
{
// fixme -- this method should be implemented for client-side TLS session resumption tickets
}
virtual ~SSL()
{
erase();
@ -972,9 +989,9 @@ namespace openvpn {
}
// like ssl() above but verify hostname against cert CommonName and/or SubjectAltName
virtual SSLAPI::Ptr ssl(const std::string& hostname)
virtual SSLAPI::Ptr ssl(const std::string* hostname, const std::string* cache_key)
{
return SSL::Ptr(new SSL(this, hostname.c_str()));
return SSL::Ptr(new SSL(this, hostname->c_str()));
}
virtual const Mode& mode() const

View File

@ -0,0 +1,186 @@
// 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/>.
#pragma once
#include <string>
#include <unordered_map>
#include <set>
#include <tuple>
#include <memory>
#include <utility>
#include <openssl/ssl.h>
#include <openvpn/common/rc.hpp>
#include <openvpn/common/msfind.hpp>
namespace openvpn {
// Client-side session cache.
// (We don't cache server-side sessions because we use TLS
// session resumption tickets which are stateless on the server).
class OpenSSLSessionCache : public RC<thread_unsafe_refcount>
{
public:
typedef RCPtr<OpenSSLSessionCache> Ptr;
OPENVPN_EXCEPTION(openssl_sess_cache_error);
// Wrapper for OpenSSL SSL_SESSION pointers that manages reference counts.
class Session
{
public:
Session(::SSL_SESSION* sess) // caller must pre-increment refcount on sess
: sess_(sess)
{
}
Session(Session&& other) noexcept
{
sess_ = other.sess_;
other.sess_ = nullptr;
}
Session& operator=(Session&& other) noexcept
{
if (sess_)
::SSL_SESSION_free(sess_);
sess_ = other.sess_;
other.sess_ = nullptr;
return *this;
}
::SSL_SESSION* openssl_session() const
{
return sess_;
}
bool operator<(const Session& rhs) const // used when Session is a std::set key
{
return sess_ < rhs.sess_;
}
explicit operator bool() const
{
return sess_ != nullptr;
}
~Session()
{
if (sess_)
::SSL_SESSION_free(sess_);
}
private:
// These methods are deleted because we have no way to increment
// an SSL_SESSION refcount until OpenSSL 1.1.
Session(const Session&) = delete;
Session& operator=(const Session&) = delete;
::SSL_SESSION* sess_;
};
class Key
{
public:
typedef std::unique_ptr<Key> UPtr;
Key(const std::string& key_arg,
OpenSSLSessionCache::Ptr cache_arg)
: key(key_arg),
cache(std::move(cache_arg))
{
//OPENVPN_LOG("OpenSSLSessionCache::Key CONSTRUCT key=" << key);
}
void commit(::SSL_SESSION* sess)
{
if (!sess)
return;
auto mi = MSF::find(cache->map, key);
if (mi)
{
/* auto ins = */ mi->second.emplace(sess);
//OPENVPN_LOG("OpenSSLSessionCache::Key::commit ADD=" << ins.second << " key=" << key);
}
else
{
//OPENVPN_LOG("OpenSSLSessionCache::Key::commit CREATE key=" << key);
auto ins = cache->map.emplace(std::piecewise_construct,
std::forward_as_tuple(key),
std::forward_as_tuple());
ins.first->second.emplace(sess);
}
}
private:
const std::string key;
OpenSSLSessionCache::Ptr cache;
};
// Remove a session from the map after calling func() on it.
// This would be a lot cleaner if we had C++17 std::set::extract().
template <typename FUNC>
void extract(const std::string& key, FUNC func)
{
auto mi = MSF::find(map, key);
if (mi)
{
//OPENVPN_LOG("OpenSSLSessionCache::Key::lookup EXISTS key=" << key);
SessionSet& ss = mi->second;
if (ss.empty())
throw openssl_sess_cache_error("internal error: SessionSet is empty");
auto ssi = ss.begin();
try {
func(ssi->openssl_session());
}
catch (...)
{
remove_session(mi, ss, ssi);
throw;
}
remove_session(mi, ss, ssi);
}
else
{
//OPENVPN_LOG("OpenSSLSessionCache::Key::lookup NOT_FOUND key=" << key);
}
}
private:
struct SessionSet : public std::set<Session>
{
};
typedef std::unordered_map<std::string, SessionSet> Map;
void remove_session(Map::iterator mi, SessionSet& ss, SessionSet::iterator ssi)
{
ss.erase(ssi);
if (ss.empty())
map.erase(mi);
}
Map map;
};
}

View File

@ -33,6 +33,7 @@
#include <openssl/ssl.h>
#include <openssl/x509v3.h>
#include <openssl/rsa.h>
#include <openssl/rand.h>
#include <openvpn/common/size.hpp>
#include <openvpn/common/exception.hpp>
@ -61,6 +62,7 @@
#include <openvpn/openssl/pki/dh.hpp>
#include <openvpn/openssl/pki/x509store.hpp>
#include <openvpn/openssl/bio/bio_memq_stream.hpp>
#include <openvpn/openssl/ssl/sess_cache.hpp>
// An SSL Context is essentially a configuration that can be used
// to generate an arbitrary number of actual SSL connections objects.
@ -111,6 +113,18 @@ namespace openvpn {
external_pki = external_pki_arg;
}
// server side
virtual void set_session_ticket_handler(TLSSessionTicketBase* session_ticket_handler_arg)
{
session_ticket_handler = session_ticket_handler_arg;
}
// client side
virtual void set_client_session_tickets(const bool v)
{
client_session_tickets = v;
}
virtual void set_private_key_password(const std::string& pwd)
{
pkey.set_private_key_password(pwd);
@ -394,6 +408,7 @@ namespace openvpn {
OpenSSLPKI::PKey pkey; // private key
OpenSSLPKI::DH dh; // diffie-hellman parameters (only needed in server mode)
ExternalPKIBase* external_pki = nullptr;
TLSSessionTicketBase* session_ticket_handler = nullptr; // server side only
Frame::Ptr frame;
int ssl_debug_level = 0;
unsigned int flags = 0; // defined in sslconsts.hpp
@ -406,6 +421,7 @@ namespace openvpn {
X509Track::ConfigSet x509_track_config;
bool local_cert_enabled = true;
bool force_aes_cbc_ciphersuites = false;
bool client_session_tickets = false;
};
// Represents an actual SSL session.
@ -430,8 +446,11 @@ namespace openvpn {
if (status == -1 && BIO_should_retry(ssl_bio))
return SSLConst::SHOULD_RETRY;
else
{
mark_no_cache();
OPENVPN_THROW(OpenSSLException, "OpenSSLContext::SSL::write_cleartext: BIO_write failed, size=" << size << " status=" << status);
}
}
else
return status;
}
@ -446,8 +465,11 @@ namespace openvpn {
if (status == -1 && BIO_should_retry(ssl_bio))
return SSLConst::SHOULD_RETRY;
else
{
mark_no_cache();
OPENVPN_THROW(OpenSSLException, "OpenSSLContext::SSL::read_cleartext: BIO_read failed, cap=" << capacity << " status=" << status);
}
}
else
return status;
}
@ -493,11 +515,20 @@ namespace openvpn {
return ssl_handshake_details(ssl);
}
virtual const AuthCert::Ptr& auth_cert() const
virtual const AuthCert::Ptr& auth_cert()
{
// Reused sessions don't call the cert verify callbacks,
// so we must use an alternative method to build authcert.
if (authcert && authcert->is_uninitialized())
rebuild_authcert();
return authcert;
}
virtual void mark_no_cache()
{
sess_cache_key.reset();
}
~SSL()
{
ssl_erase();
@ -535,7 +566,7 @@ namespace openvpn {
}
private:
SSL(const OpenSSLContext& ctx, const char *hostname)
SSL(const OpenSSLContext& ctx, const std::string* hostname, const std::string* cache_key)
{
ssl_clear();
try {
@ -552,7 +583,7 @@ namespace openvpn {
{
X509_VERIFY_PARAM *param = SSL_get0_param(ssl);
X509_VERIFY_PARAM_set_hostflags(param, 0);
X509_VERIFY_PARAM_set1_host(param, hostname, 0);
X509_VERIFY_PARAM_set1_host(param, hostname->c_str(), 0);
}
// init BIOs
@ -572,6 +603,17 @@ namespace openvpn {
}
else if (ctx.config->mode.is_client())
{
if (cache_key && ctx.sess_cache)
{
// see if a cached session already exists for our cache_key
ctx.sess_cache->extract(*cache_key, [this](SSL_SESSION* sess) {
if (!SSL_set_session(ssl, sess))
throw OpenSSLException("SSL_set_session failed");
});
// cache the session before its end-of-life if no errors occur
sess_cache_key.reset(new OpenSSLSessionCache::Key(*cache_key, ctx.sess_cache));
}
SSL_set_connect_state(ssl);
if (ctx.config->flags & SSLConst::ENABLE_SNI)
if (SSL_set_tlsext_host_name(ssl, hostname) != 1)
@ -599,6 +641,27 @@ namespace openvpn {
}
}
void rebuild_authcert()
{
authcert.reset(new AuthCert());
::X509 *cert = SSL_get_peer_certificate(ssl);
if (cert)
{
// save the issuer cert fingerprint
static_assert(sizeof(AuthCert::issuer_fp) == SHA_DIGEST_LENGTH, "size inconsistency");
unsigned int md_len = sizeof(AuthCert::issuer_fp);
X509_digest (cert, EVP_sha1 (), authcert->issuer_fp, &md_len);
// save the Common Name
authcert->cn = x509_get_field(cert, NID_commonName);
// save the leaf cert serial number
const ASN1_INTEGER *ai = X509_get_serialNumber(cert);
authcert->sn = ai ? ASN1_INTEGER_get(ai) : -1;
}
}
// Indicate no data available for our custom SSLv23 method
static int ssl_pending_override(const ::SSL *)
{
@ -632,6 +695,8 @@ namespace openvpn {
}
X509_free (cert);
}
if (SSL_session_reused(c_ssl))
os << " [REUSED]";
return os.str();
}
@ -643,6 +708,7 @@ namespace openvpn {
ct_in = nullptr;
ct_out = nullptr;
overflow = false;
sess_cache_key.reset();
}
void ssl_erase()
@ -657,7 +723,14 @@ namespace openvpn {
if (ssl_bio)
BIO_free_all(ssl_bio);
if (ssl)
{
if (sess_cache_key)
{
SSL_set_shutdown(ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN);
sess_cache_key->commit(SSL_get1_session(ssl));
}
SSL_free(ssl);
}
openssl_clear_error_stack();
ssl_clear();
}
@ -702,6 +775,7 @@ namespace openvpn {
BIO *ct_in; // write ciphertext to here
BIO *ct_out; // read ciphertext from here
AuthCert::Ptr authcert;
OpenSSLSessionCache::Key::UPtr sess_cache_key; // client-side only
bool ssl_bio_linkage;
bool overflow;
@ -923,19 +997,48 @@ namespace openvpn {
OPENVPN_THROW(ssl_context_error, "OpenSSLContext: unknown config->mode");
// Set SSL options
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
if (!(config->flags & SSLConst::NO_VERIFY_PEER))
{
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
config->mode.is_client() ? verify_callback_client : verify_callback_server);
SSL_CTX_set_verify_depth(ctx, 16);
}
long sslopt = SSL_OP_SINGLE_DH_USE | SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_COMPRESSION | SSL_OP_NO_TICKET;
long sslopt = SSL_OP_SINGLE_DH_USE | SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_COMPRESSION;
/* Disable SSLv2 and SSLv3, might be a noop but does not hurt */
sslopt |= SSL_OP_NO_SSLv2;
sslopt |= SSL_OP_NO_SSLv3;
if (config->mode.is_server())
{
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
if (config->session_ticket_handler)
{
static const char sess_id_context[] = "OpenVPN";
if (!SSL_CTX_set_session_id_context(ctx, (unsigned char *)sess_id_context, sizeof(sess_id_context) - 1))
throw OpenSSLException("OpenSSLContext: SSL_CTX_set_session_id_context failed");
if (!SSL_CTX_set_tlsext_ticket_key_cb(ctx, tls_ticket_key_callback))
throw OpenSSLException("OpenSSLContext: SSL_CTX_set_tlsext_ticket_key_cb failed");
}
else
sslopt |= SSL_OP_NO_TICKET;
}
else
{
if (config->client_session_tickets)
{
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_CLIENT);
sess_cache.reset(new OpenSSLSessionCache);
}
else
{
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
sslopt |= SSL_OP_NO_TICKET;
}
}
/* mbed TLS also ignores tls version when force aes cbc cipher suites is on */
if (!config->force_aes_cbc_ciphersuites)
{
@ -1084,13 +1187,13 @@ namespace openvpn {
// create a new SSL instance
virtual SSLAPI::Ptr ssl()
{
return SSL::Ptr(new SSL(*this, nullptr));
return SSL::Ptr(new SSL(*this, nullptr, nullptr));
}
// like ssl() above but verify hostname against cert CommonName and/or SubjectAltName
virtual SSLAPI::Ptr ssl(const std::string& hostname)
virtual SSLAPI::Ptr ssl(const std::string* hostname, const std::string* cache_key)
{
return SSL::Ptr(new SSL(*this, hostname.c_str()));
return SSL::Ptr(new SSL(*this, hostname, cache_key));
}
void update_trust(const CertCRLList& cc)
@ -1573,6 +1676,92 @@ namespace openvpn {
}
}
static int tls_ticket_key_callback(::SSL *ssl,
unsigned char key_name[16],
unsigned char iv[EVP_MAX_IV_LENGTH],
::EVP_CIPHER_CTX *ctx,
::HMAC_CTX *hctx,
int enc)
{
// get OpenSSLContext
const OpenSSLContext* self = (OpenSSLContext*) ssl->ctx->app_verify_arg;
if (!self)
return -1;
// get user-defined session ticket handler
TLSSessionTicketBase* t = self->config->session_ticket_handler;
if (!t)
return -1;
if (enc)
{
// create new ticket
TLSSessionTicketBase::Name name;
TLSSessionTicketBase::Key key;
switch (t->create_session_ticket_key(name, key))
{
case TLSSessionTicketBase::TICKET_AVAILABLE:
if (!RAND_bytes(iv, EVP_MAX_IV_LENGTH))
return -1;
if (!tls_ticket_init_cipher_hmac(key, iv, ctx, hctx, enc))
return -1;
static_assert(TLSSessionTicketBase::Name::SIZE == 16, "unexpected name size");
name.copy_to(key_name);
//OPENVPN_LOG("tls_ticket_key_callback: created ticket");
return 1;
case TLSSessionTicketBase::NO_TICKET:
case TLSSessionTicketBase::TICKET_EXPIRING: // doesn't really make sense for enc==1?
//OPENVPN_LOG("tls_ticket_key_callback: create: no ticket or expiring ticket");
return 0;
default:
//OPENVPN_LOG("tls_ticket_key_callback: create: bad ticket");
return -1;
}
}
else
{
// lookup existing ticket
static_assert(TLSSessionTicketBase::Name::SIZE == 16, "unexpected name size");
const TLSSessionTicketBase::Name name(key_name);
TLSSessionTicketBase::Key key;
switch (t->lookup_session_ticket_key(name, key))
{
case TLSSessionTicketBase::TICKET_AVAILABLE:
if (!tls_ticket_init_cipher_hmac(key, iv, ctx, hctx, enc))
return -1;
//OPENVPN_LOG("tls_ticket_key_callback: found ticket");
return 1;
case TLSSessionTicketBase::TICKET_EXPIRING:
if (!tls_ticket_init_cipher_hmac(key, iv, ctx, hctx, enc))
return -1;
//OPENVPN_LOG("tls_ticket_key_callback: expiring ticket");
return 2;
case TLSSessionTicketBase::NO_TICKET:
//OPENVPN_LOG("tls_ticket_key_callback: lookup: no ticket");
return 0;
default:
//OPENVPN_LOG("tls_ticket_key_callback: lookup: bad ticket");
return -1;
}
}
}
static bool tls_ticket_init_cipher_hmac(const TLSSessionTicketBase::Key& key,
unsigned char iv[EVP_MAX_IV_LENGTH],
::EVP_CIPHER_CTX *ctx,
::HMAC_CTX *hctx,
const int enc)
{
static_assert(TLSSessionTicketBase::Key::CIPHER_KEY_SIZE == 32, "unexpected cipher key size");
if (!EVP_CipherInit_ex(ctx, EVP_aes_256_cbc(), nullptr, key.cipher_value(), iv, enc))
return false;
if (!HMAC_Init_ex(hctx, key.hmac_value(), TLSSessionTicketBase::Key::HMAC_KEY_SIZE, EVP_sha256(), nullptr))
return false;
return true;
}
void erase()
{
if (epki)
@ -1590,6 +1779,7 @@ namespace openvpn {
Config::Ptr config;
SSL_CTX* ctx = nullptr;
ExternalPKIImpl* epki = nullptr;
OpenSSLSessionCache::Ptr sess_cache; // client-side only
};
int OpenSSLContext::SSL::mydata_index = -1;

205
openvpn/ssl/sess_ticket.hpp Normal file
View File

@ -0,0 +1,205 @@
// 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 <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 {
// Abstract base class used to provide an interface for TLS
// Session Ticket keying originally described by RFC 5077.
class TLSSessionTicketBase
{
public:
enum Status {
NO_TICKET,
TICKET_AVAILABLE,
TICKET_EXPIRING,
ERROR,
};
class Name
{
public:
static constexpr size_t SIZE = 16;
Name() {} // note that default constructor leaves object in an undefined state
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) == -1;
}
std::string to_string() const
{
return "TLSTicketName[" + b64() + ']';
}
std::string b64() const
{
return base64->encode(value_, SIZE);
}
void copy_to(unsigned char name[SIZE]) const
{
std::memcpy(name, 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:
unsigned char value_[SIZE];
};
class Key
{
public:
static constexpr size_t CIPHER_KEY_SIZE = 32;
static constexpr size_t HMAC_KEY_SIZE = 16;
Key() {} // note that default constructor leaves object in an undefined state
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);
}
std::string to_string() const
{
return "TLSTicketKey[cipher=" + cipher_b64() + " hmac=" + hmac_b64() + ']';
}
const unsigned char* cipher_value() const
{
return cipher_value_;
}
const unsigned char* hmac_value() const
{
return hmac_value_;
}
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);
}
private:
unsigned char cipher_value_[CIPHER_KEY_SIZE];
unsigned char hmac_value_[HMAC_KEY_SIZE];
};
// method sets name and key
virtual Status create_session_ticket_key(Name& name, Key& key) const = 0;
// method is passed name and returns key
virtual Status lookup_session_ticket_key(const Name& name, Key& key) 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 Exception(std::string("TLSSessionTicketBase: base64 decode for ") + title + ": " + std::string(e.what()));
}
if (srcbuf.size() != outlen)
throw Exception(std::string("TLSSessionTicketBase: 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

View File

@ -41,6 +41,7 @@
#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 {
@ -65,7 +66,8 @@ namespace openvpn {
virtual bool read_ciphertext_ready() const = 0;
virtual BufferPtr read_ciphertext() = 0;
virtual std::string ssl_handshake_details() const = 0;
virtual const AuthCert::Ptr& auth_cert() const = 0;
virtual const AuthCert::Ptr& auth_cert() = 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;
@ -87,8 +89,9 @@ namespace openvpn {
// create a new SSLAPI instance
virtual SSLAPI::Ptr ssl() = 0;
// like ssl() above but verify hostname against cert CommonName and/or SubjectAltName
virtual SSLAPI::Ptr ssl(const std::string& hostname) = 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;
@ -140,6 +143,8 @@ namespace openvpn {
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_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;