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 275cf80efb..7db7a009b0
7db7a009b0 proto: Client complains about stub compressors 390154d0e4 Update Build instructions for OSX 1b92069834 deps: Update to mbedtls-2.7.12 8cab79540d compression: Extend compression alert to include server pushes 67b4641a99 CompressContext: Add is_any_stub() method cdf9e7bece compression: Issue an Event if compression is activated fa38064403 build script: added a new PROF type "auto" that tries to automatically determine the local platform 7ce7b52b7c MTRand: added OPENVPN_INSECURE_RANDOM compile flag that allows MTRand to masquerade as a secure RNG 85e7e49f72 MTRand: added constructor accepting an initialization seed 1fa3229a10 IPv4, IPv6: added #include <openvpn/common/hash.hpp> 48e9217d26 vcxproj: add missing header file d2a2601b2f Wintun: unmap ring buffers e320bc63ff openssl: Improve OpenSSLContext fencing against multiple declarations 2f8fe2d318 openssl: Missing inline keyword in a couple of compat functions 32b984c0ff enum_dir: use a function template 725ee04593 VPNServerNetblock::Netblock::to_string(): show prefix_len 409d1c52b8 ManClientInstance::Send::describe_user(): added bool show_userprop parameter e05fc16b20 string::indent(): try to fix all the corner cases 4e1645ea80 RunContext: mark virtual Stop* async_stop() with override attribute e8b31c5454 cli: advertise "openurl" as supported SSO method 80b45731eb ICMPv6: added DEST_UNREACH code 679003094d AsioTimerSafe: refactor to allow as drop-in replacement for AsioTimer f7845578f1 RunContext: check for halt in timer closure 84483eda25 AsioPolySock: add support for socket shutdown 1b3402aec3 tcplinkcommon.hpp: added missing include 2e26c7565c time: added nanotime_t typedef c3c8ab7f6b string: added additional detail to split() comment 95ce4f22c8 string: added to_delim() method then redefined first_line() method to use it 448218b1e1 string: added add_leading() method e3b0bf4f5c MSF iterator: allow conversion from ordinary iterator and added exists() method 11412ac50a AsioPolySock: in remote_endpoint_str() method, test for alt_routing_enabled() 9fb4e705f9 Added TimeSkew to skew a time duration by a random flux 7496383002 write_binary_atomic: reduce the length of the temporary filename b31d9c0191 auth-token-user: increase size limit to 340 chars c82644c03a Added BufferLineIterator 115cb656b6 RandomAPI: added randbyte() and randbool() methods 4fa8348689 RunContext: ASIO SIGNAL message now shows signal name rather than number ebfce58513 Added StaticBuffer, a constant-length Buffer for writing that cannot be extended c8f9cb88a4 string::split(): call reserve() on return vector f15e566065 read_binary_unix_fast: should return an int (i.e. errno), not a bool 60501b4513 random: factor out rand32_distribute() from RandomAPI::randrange32() 90123495a5 wintun: get device interfaces list only once ec790df73b wintun: read packets in bulk 0f85d3f729 wintun: use correct io_context when performing initial read a6151cdeab wintun: use auto-reset events 29acfd95f3 libs: update ASIO to 1.14.0 438a0ef287 Remove outdated and unused android build files e9df57969f Merge remote-tracking branch 'origin/released' 44725ad094 ssl: Fix building with OpenSSL 1.0.2 efe3f1f635 version: Reset version reference for git master 8c79c06d94 Make tls-crypt/tls-cryptv2 compile with multiple compilation units 4d18aaeb88 Fix LLVM warnings reported during OS X build 8c9496bb4d Use const_cast for SSL_session_reused 33be562a39 Add missing override keywords to openssl/sslctx.hpp 2c5435a000 dcocli: use compile time define for Tun methods instead of hardcoded iproute 7c39088f00 Allow overriding reported HW_ADDR and support IV_PLAT_VER 7bb1ea19ee Move sending IV_UI_VER and IV_SSO to build_peer_info 23959fa705 Add reporting of IV_SSL_VER 63ab5b5e46 Only initialise static member in OpenSSLContext once ecebb40304 Merge remote-tracking branch 'origin/qa' 52c9702502 wintun: replace volatiles with atomics d720c7104c appveyor: install Strawberry perl 60a253a7ef appveyor: update to VS2019 48f2b5100b wintun: support for privilege separation 6f266be3d8 wintun: ring buffers support baa1ce2ccf vcxproj: bump VS version to 2019 98bfd037e3 tun/win: factor out ClientConfig into separate header aeb5ce0ad7 wintun: open device with SetupAPI 3998d303ce Finalizing the OpenVPN 3 Core library 3.3 release 728733aee7 deps/mbedtls: rebase "enable unsupported critical extensions" patch 43e36ca45a lib-version: update to mbedtls-2.7.11 4dbcd85e50 openssl/cipher.hpp: add missing include <compat.hpp> 69d72ed64f DCOTransport: Fix server side specific trunk handling ff732e3b5d Fix OpenVPN Core build with OpenSSL 1.1.0 0da42f393f Do not use deprecated OpenSSL 1.1.0 methods 35062c0b60 travis.yml: update environment 47046cf6d2 Merge branch 'qa' 6933c395a4 [OVPN3-423] cliconnect.hpp: fix reconnect on Windows after sleep 462c36c813 random_subnet(): added comment ac1d447156 IP::Addr::from_byte_string(): fixed bug for IPv6 case d6eaea3468 string::split(): minor implementation tweaks ca15b7cdf4 hexstr: added dump_hex() variant accepting void * 0e61a2afd7 SessionIDType::find_weak: added conflict parameter 089aec00b1 DCOTransport: new routing code for trunk links 5befbd430f build: added CAP=1 -- build with libcap eb85ada21e signals: added trivial signal_name() function f89013ef92 RunContext: don't try to catch SIGQUIT by default e0ee540135 SessionIDType: added hash() method f0e1f8aa42 logging: added basic components for logrotate fbb0c81f29 UMask: added UMaskDaemon, a umask context object appropriate for daemons 1c7bac90d9 build script: when building with DEBUG=1 on Linux, use -ggdb instead of -g 73cce80e43 OpenSSL: added openssl_reseed_rng() function 25780cf798 OpenSSL: fixed some memory leaks in CipherContextGCM and TokenEncrypt 168dba95f5 OpenSSL: define OPENSSL_SERVER_SNI when OpenSSL version is at least 1.1 84e78d8fed SNI: added OpenVPN client support for SNI (currently OpenSSL only) 310766b270 build: added MTLS_DIST setting 4eaa46a879 MbedTLS: added MBEDTLS_DISABLE_NAME_CONSTRAINTS preprocessor flag 16226d1b05 OpenSSLSign: updated for OpenSSL 1.1 aed0678c96 SSL: added SNI::Metadata, an abstract base class for packaging app-specific SNI metadata in AuthCert 001b731fe2 SNI: create SNI namespace and rename SNIHandlerBase -> SNI::HandlerBase 4bd5869305 README.rst: Make Windows-specific build steps up to date. ac365ee977 wintun: support for 0.4 9245056a2a wintun: support for 0.3 b73d484950 mbedtls: throw exception on unsupported SSL:Const::PEER_CERT_OPTIONAL option 1d6bae4b5b tcplinkcommon: bubble up real exception error c18c8bd156 tcpcli: ensure SSL Factory survives as long as TLS link 4192193087 tls: parse and load TLS specific CA 2a19b7fcff win/tuncli.hpp: fix Wintun padding calculation 44cb9f44da appveyor: make ReleaseOpenSSL default configuration 5485de19a2 win/impersonate: refactor impersonate logic 29a655147b win/tunsetup.hpp: remove unneeded parameter 61794b0efd win: link OpenSSL dynamically e569b84465 win/tuncli.hpp: fix indentation 374c57e708 frame_init.hpp: tweak wintun read buf size c3c45c9b38 tun: added Error::TUN_HALT for tun_error() signaling acd7af5e9a RandomAPI: added randrange32() method c1a7f8cc68 std::clamp() is useful but only available in C++17 and up, so we add our own clamp() f8c71ef1ce Minor change to Error::INACTIVE_TIMEOUT handler 3202ab5fce OpenSSLSign: renamed OpenSSLPKI::X509Base to OpenSSLPKI::X509 to conform to changes in OpenSSLPKI 8d767febb5 ReachabilityBase: added virtual destructor 6a4826965f MbedTLS: update json_override() prototype bee0d8d187 SSL: added SSLConst::SEND_CLIENT_CA_LIST server-side flag and implemented for OpenSSL 5eb39c1dea AuthCert: save the SNI name 3b34449d0e SSLAPI: auth_cert() can now be const a672e91631 SNI server-side: support additional JSON configuration settings 95e761f3cc OpenSSL PKI cleanup d5eb77c53c AuthCert::Fail cleanup 6e98b9aadc SSLAPI: move PKType from SSLConfigAPI into standalone header to avoid dependency inversion bbae814864 OpenSSL: added SNI implementation 5def1d23ab OpenSSLContext: in constructor, removed redundant if statement 1a0747e783 OpenSSLContext: in constructor, consolidate sslopt fixed flags eef9868816 OpenSSLContext::SSL::ssl_handshake_details(): include leaf-cert CN in details f9631cd90f AuthCert::Fail: use std::string for the reason string (instead of const char *) a17b77641f OpenSSLPKI::X509: copy constructor doesn't need erase() and define X509::Ptr 78cae5bb52 OpenSSLPKI::DH: copy constructor doesn't need erase() c0d43a4153 RCPtr: added static_pointer_cast() method 34a3f264f5 [OVPN-314] Add support for signalling SSO support via IV_SSO 7d112eb3e5 cli: enable utf8 console output 980ef1eff8 win/call.hpp: re-encode command output to utf8 fddb440e99 unicode.hpp: customize utf16 conversion routine 4d7c12ac4d [OVPN3-405] Support for non-ASCII profile path on Windows git-subtree-dir: Sources/OpenVPNAdapter/Libraries/Vendors/openvpn git-subtree-split: 7db7a009b0b4eca0fc3733c99c50aff7f7c2556f
This commit is contained in:
@@ -97,7 +97,7 @@ inline EVP_MD_CTX *EVP_MD_CTX_new()
|
||||
return new EVP_MD_CTX();
|
||||
}
|
||||
|
||||
void EVP_MD_CTX_free(EVP_MD_CTX *ctx)
|
||||
inline void EVP_MD_CTX_free(EVP_MD_CTX *ctx)
|
||||
{
|
||||
delete ctx;
|
||||
}
|
||||
@@ -211,10 +211,10 @@ inline int RSA_meth_set_priv_enc(RSA_METHOD *meth,
|
||||
return 1;
|
||||
}
|
||||
|
||||
int RSA_meth_set_priv_dec(RSA_METHOD *meth,
|
||||
int (*priv_dec)(int flen, const unsigned char *from,
|
||||
unsigned char *to, RSA *rsa,
|
||||
int padding))
|
||||
inline int RSA_meth_set_priv_dec(RSA_METHOD *meth,
|
||||
int (*priv_dec)(int flen, const unsigned char *from,
|
||||
unsigned char *to, RSA *rsa,
|
||||
int padding))
|
||||
{
|
||||
meth->rsa_priv_dec = priv_dec;
|
||||
return 1;
|
||||
@@ -307,6 +307,19 @@ inline void RSA_get0_key(const RSA *rsa, const BIGNUM **n, const BIGNUM **e, con
|
||||
/* Renamed in OpenSSL 1.1 */
|
||||
#define X509_get0_pubkey X509_get_pubkey
|
||||
#define RSA_F_RSA_OSSL_PRIVATE_ENCRYPT RSA_F_RSA_EAY_PRIVATE_ENCRYPT
|
||||
|
||||
/*
|
||||
* EVP_CIPHER_CTX_init and EVP_CIPHER_CTX_cleanup are both replaced by
|
||||
* EVP_CIPHER_CTX_reset in OpenSSL 1.1 but replacing them both with
|
||||
* reset is wrong for older version. The man page mention cleanup
|
||||
* being officially removed and init to be an alias for reset.
|
||||
*
|
||||
* So we only use reset as alias for init in older versions.
|
||||
*
|
||||
* EVP_CIPHER_CTX_free already implicitly calls EVP_CIPHER_CTX_cleanup in
|
||||
* 1.0.2, so we can avoid using the old API.
|
||||
*/
|
||||
#define EVP_CIPHER_CTX_reset EVP_CIPHER_CTX_init
|
||||
#endif
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10101000L
|
||||
@@ -333,4 +346,4 @@ inline const BIGNUM *DSA_get0_p(const DSA *d)
|
||||
DSA_get0_pqg(d, &p, nullptr, nullptr);
|
||||
return p;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include <openvpn/crypto/static_key.hpp>
|
||||
#include <openvpn/crypto/cryptoalgs.hpp>
|
||||
#include <openvpn/openssl/util/error.hpp>
|
||||
#include <openvpn/openssl/compat.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace OpenSSLCrypto {
|
||||
@@ -75,7 +76,7 @@ namespace openvpn {
|
||||
throw openssl_cipher_mode_error();
|
||||
erase();
|
||||
ctx = EVP_CIPHER_CTX_new ();
|
||||
EVP_CIPHER_CTX_init (ctx);
|
||||
EVP_CIPHER_CTX_reset (ctx);
|
||||
if (!EVP_CipherInit_ex (ctx, cipher_type(alg), nullptr, key, nullptr, mode))
|
||||
{
|
||||
openssl_clear_error_stack();
|
||||
@@ -177,7 +178,6 @@ namespace openvpn {
|
||||
{
|
||||
if (initialized)
|
||||
{
|
||||
EVP_CIPHER_CTX_cleanup(ctx);
|
||||
EVP_CIPHER_CTX_free (ctx);
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ namespace openvpn {
|
||||
if (ckeysz > keysize)
|
||||
throw openssl_gcm_error("insufficient key material");
|
||||
ctx = EVP_CIPHER_CTX_new();
|
||||
EVP_CIPHER_CTX_init(ctx);
|
||||
EVP_CIPHER_CTX_reset(ctx);
|
||||
switch (mode)
|
||||
{
|
||||
case ENCRYPT:
|
||||
@@ -223,15 +223,17 @@ namespace openvpn {
|
||||
{
|
||||
if (initialized)
|
||||
{
|
||||
EVP_CIPHER_CTX_cleanup(ctx);
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
initialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
void check_initialized() const
|
||||
{
|
||||
#ifdef OPENVPN_ENABLE_ASSERT
|
||||
if (unlikely(!initialized))
|
||||
throw openssl_gcm_error("uninitialized");
|
||||
#endif
|
||||
}
|
||||
|
||||
bool initialized;
|
||||
|
||||
+57
-48
@@ -21,8 +21,7 @@
|
||||
|
||||
// Wrap an OpenSSL X509_CRL object
|
||||
|
||||
#ifndef OPENVPN_OPENSSL_PKI_CRL_H
|
||||
#define OPENVPN_OPENSSL_PKI_CRL_H
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -32,16 +31,18 @@
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/openssl/util/error.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace OpenSSLPKI {
|
||||
|
||||
class CRL : public RC<thread_unsafe_refcount>
|
||||
class CRL
|
||||
{
|
||||
public:
|
||||
CRL() : crl_(nullptr) {}
|
||||
CRL()
|
||||
: crl_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
explicit CRL(const std::string& crl_txt)
|
||||
: crl_(nullptr)
|
||||
@@ -50,27 +51,48 @@ namespace openvpn {
|
||||
}
|
||||
|
||||
CRL(const CRL& other)
|
||||
: crl_(nullptr)
|
||||
: crl_(dup(other.crl_))
|
||||
{
|
||||
assign(other.crl_);
|
||||
}
|
||||
|
||||
void operator=(const CRL& other)
|
||||
CRL(CRL&& other) noexcept
|
||||
: crl_(other.crl_)
|
||||
{
|
||||
assign(other.crl_);
|
||||
other.crl_ = nullptr;
|
||||
}
|
||||
|
||||
CRL& operator=(const CRL& other)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
erase();
|
||||
crl_ = dup(other.crl_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
CRL& operator=(CRL&& other) noexcept
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
erase();
|
||||
crl_ = other.crl_;
|
||||
other.crl_ = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool defined() const { return crl_ != nullptr; }
|
||||
X509_CRL* obj() const { return crl_; }
|
||||
::X509_CRL* obj() const { return crl_; }
|
||||
|
||||
void parse_pem(const std::string& crl_txt)
|
||||
{
|
||||
BIO *bio = BIO_new_mem_buf(const_cast<char *>(crl_txt.c_str()), crl_txt.length());
|
||||
BIO *bio = ::BIO_new_mem_buf(const_cast<char *>(crl_txt.c_str()), crl_txt.length());
|
||||
if (!bio)
|
||||
throw OpenSSLException();
|
||||
|
||||
X509_CRL *crl = PEM_read_bio_X509_CRL(bio, nullptr, nullptr, nullptr);
|
||||
BIO_free(bio);
|
||||
::X509_CRL *crl = ::PEM_read_bio_X509_CRL(bio, nullptr, nullptr, nullptr);
|
||||
::BIO_free(bio);
|
||||
if (!crl)
|
||||
throw OpenSSLException("CRL::parse_pem");
|
||||
|
||||
@@ -82,19 +104,19 @@ namespace openvpn {
|
||||
{
|
||||
if (crl_)
|
||||
{
|
||||
BIO *bio = BIO_new(BIO_s_mem());
|
||||
const int ret = PEM_write_bio_X509_CRL(bio, crl_);
|
||||
BIO *bio = ::BIO_new(BIO_s_mem());
|
||||
const int ret = ::PEM_write_bio_X509_CRL(bio, crl_);
|
||||
if (ret == 0)
|
||||
{
|
||||
BIO_free(bio);
|
||||
::BIO_free(bio);
|
||||
throw OpenSSLException("CRL::render_pem");
|
||||
}
|
||||
|
||||
{
|
||||
char *temp;
|
||||
const int buf_len = BIO_get_mem_data(bio, &temp);
|
||||
const int buf_len = ::BIO_get_mem_data(bio, &temp);
|
||||
std::string ret = std::string(temp, buf_len);
|
||||
BIO_free(bio);
|
||||
::BIO_free(bio);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -102,59 +124,46 @@ namespace openvpn {
|
||||
return "";
|
||||
}
|
||||
|
||||
void erase()
|
||||
{
|
||||
if (crl_)
|
||||
{
|
||||
X509_CRL_free(crl_);
|
||||
crl_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
~CRL()
|
||||
{
|
||||
erase();
|
||||
}
|
||||
|
||||
private:
|
||||
void erase()
|
||||
{
|
||||
if (crl_)
|
||||
::X509_CRL_free(crl_);
|
||||
}
|
||||
|
||||
static X509_CRL *dup(const X509_CRL *crl)
|
||||
{
|
||||
if (crl)
|
||||
{
|
||||
return X509_CRL_dup(const_cast<X509_CRL *>(crl));
|
||||
}
|
||||
return ::X509_CRL_dup(const_cast<X509_CRL *>(crl));
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void assign(const X509_CRL *crl)
|
||||
{
|
||||
erase();
|
||||
crl_ = dup(crl);
|
||||
}
|
||||
|
||||
X509_CRL *crl_;
|
||||
::X509_CRL *crl_;
|
||||
};
|
||||
|
||||
typedef RCPtr<CRL> CRLPtr;
|
||||
|
||||
class CRLList : public std::vector<CRLPtr>
|
||||
class CRLList : public std::vector<CRL>
|
||||
{
|
||||
public:
|
||||
typedef CRL Item;
|
||||
typedef CRLPtr ItemPtr;
|
||||
typedef X509 CRL;
|
||||
|
||||
bool defined() const { return !empty(); }
|
||||
bool defined() const
|
||||
{
|
||||
return !empty();
|
||||
}
|
||||
|
||||
std::string render_pem() const
|
||||
{
|
||||
std::string ret;
|
||||
for (const_iterator i = begin(); i != end(); ++i)
|
||||
ret += (*i)->render_pem();
|
||||
for (const auto &e : *this)
|
||||
ret += e.render_pem();
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
}
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_OPENSSL_PKI_CRL_H
|
||||
}
|
||||
|
||||
+44
-29
@@ -21,8 +21,7 @@
|
||||
|
||||
// Wrap an OpenSSL DH object
|
||||
|
||||
#ifndef OPENVPN_OPENSSL_PKI_DH_H
|
||||
#define OPENVPN_OPENSSL_PKI_DH_H
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
@@ -57,7 +56,10 @@ namespace openvpn {
|
||||
class DH
|
||||
{
|
||||
public:
|
||||
DH() : dh_(nullptr) {}
|
||||
DH()
|
||||
: dh_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
explicit DH(const std::string& dh_txt)
|
||||
: dh_(nullptr)
|
||||
@@ -66,14 +68,34 @@ namespace openvpn {
|
||||
}
|
||||
|
||||
DH(const DH& other)
|
||||
: dh_(nullptr)
|
||||
{
|
||||
assign(other.dh_);
|
||||
dup(other.dh_);
|
||||
}
|
||||
|
||||
DH(DH&& other) noexcept
|
||||
: dh_(other.dh_)
|
||||
{
|
||||
other.dh_ = nullptr;
|
||||
}
|
||||
|
||||
void operator=(const DH& other)
|
||||
{
|
||||
assign(other.dh_);
|
||||
if (this != &other)
|
||||
{
|
||||
erase();
|
||||
dup(other.dh_);
|
||||
}
|
||||
}
|
||||
|
||||
DH& operator=(DH&& other) noexcept
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
erase();
|
||||
dh_ = other.dh_;
|
||||
other.dh_ = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool defined() const { return dh_ != nullptr; }
|
||||
@@ -81,12 +103,12 @@ namespace openvpn {
|
||||
|
||||
void parse_pem(const std::string& dh_txt)
|
||||
{
|
||||
BIO *bio = BIO_new_mem_buf(const_cast<char *>(dh_txt.c_str()), dh_txt.length());
|
||||
BIO *bio = ::BIO_new_mem_buf(const_cast<char *>(dh_txt.c_str()), dh_txt.length());
|
||||
if (!bio)
|
||||
throw OpenSSLException();
|
||||
|
||||
::DH *dh = PEM_read_bio_DHparams(bio, nullptr, nullptr, nullptr);
|
||||
BIO_free(bio);
|
||||
::DH *dh = ::PEM_read_bio_DHparams(bio, nullptr, nullptr, nullptr);
|
||||
::BIO_free(bio);
|
||||
if (!dh)
|
||||
throw OpenSSLException("DH::parse_pem");
|
||||
|
||||
@@ -98,19 +120,19 @@ namespace openvpn {
|
||||
{
|
||||
if (dh_)
|
||||
{
|
||||
BIO *bio = BIO_new(BIO_s_mem());
|
||||
const int ret = PEM_write_bio_DHparams(bio, dh_);
|
||||
BIO *bio = ::BIO_new(BIO_s_mem());
|
||||
const int ret = ::PEM_write_bio_DHparams(bio, dh_);
|
||||
if (ret == 0)
|
||||
{
|
||||
BIO_free(bio);
|
||||
::BIO_free(bio);
|
||||
throw OpenSSLException("DH::render_pem");
|
||||
}
|
||||
|
||||
{
|
||||
char *temp;
|
||||
const int buf_len = BIO_get_mem_data(bio, &temp);
|
||||
const int buf_len = ::BIO_get_mem_data(bio, &temp);
|
||||
std::string ret = std::string(temp, buf_len);
|
||||
BIO_free(bio);
|
||||
::BIO_free(bio);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -118,31 +140,24 @@ namespace openvpn {
|
||||
return "";
|
||||
}
|
||||
|
||||
void erase()
|
||||
{
|
||||
if (dh_)
|
||||
{
|
||||
DH_free(dh_);
|
||||
dh_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
~DH()
|
||||
{
|
||||
erase();
|
||||
}
|
||||
|
||||
private:
|
||||
void assign(const ::DH *dh)
|
||||
void erase()
|
||||
{
|
||||
if (dh_)
|
||||
::DH_free(dh_);
|
||||
}
|
||||
|
||||
void dup(const ::DH *dh)
|
||||
{
|
||||
erase();
|
||||
dh_ = DH_private::dup(dh);
|
||||
}
|
||||
|
||||
::DH *dh_;
|
||||
};
|
||||
}
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_OPENSSL_PKI_DH_H
|
||||
|
||||
}
|
||||
|
||||
@@ -21,10 +21,10 @@
|
||||
|
||||
// Wrap an OpenSSL EVP_PKEY object
|
||||
|
||||
#ifndef OPENVPN_OPENSSL_PKI_PKEY_H
|
||||
#define OPENVPN_OPENSSL_PKI_PKEY_H
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/bio.h>
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/openssl/util/error.hpp>
|
||||
#include <openvpn/pki/pktype.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace OpenSSLPKI {
|
||||
@@ -39,7 +40,10 @@ namespace openvpn {
|
||||
class PKey
|
||||
{
|
||||
public:
|
||||
PKey() : pkey_(nullptr) {}
|
||||
PKey()
|
||||
: pkey_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
PKey(const std::string& pkey_txt, const std::string& title)
|
||||
: pkey_(nullptr)
|
||||
@@ -48,45 +52,69 @@ namespace openvpn {
|
||||
}
|
||||
|
||||
PKey(const PKey& other)
|
||||
: pkey_(nullptr)
|
||||
: pkey_(dup(other.pkey_)),
|
||||
priv_key_pwd(other.priv_key_pwd)
|
||||
{
|
||||
assign(other.pkey_);
|
||||
}
|
||||
|
||||
void operator=(const PKey& other)
|
||||
PKey(PKey&& other) noexcept
|
||||
: pkey_(other.pkey_),
|
||||
priv_key_pwd(std::move(other.priv_key_pwd))
|
||||
{
|
||||
assign(other.pkey_);
|
||||
priv_key_pwd = other.priv_key_pwd;
|
||||
other.pkey_ = nullptr;
|
||||
}
|
||||
|
||||
PKey& operator=(const PKey& other)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
erase();
|
||||
pkey_ = dup(other.pkey_);
|
||||
priv_key_pwd = other.priv_key_pwd;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
PKey& operator=(PKey&& other) noexcept
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
erase();
|
||||
pkey_ = other.pkey_;
|
||||
other.pkey_ = nullptr;
|
||||
priv_key_pwd = std::move(other.priv_key_pwd);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool defined() const { return pkey_ != nullptr; }
|
||||
EVP_PKEY* obj() const { return pkey_; }
|
||||
::EVP_PKEY* obj() const { return pkey_; }
|
||||
|
||||
SSLConfigAPI::PKType key_type() const
|
||||
PKType::Type key_type() const
|
||||
{
|
||||
switch (EVP_PKEY_id(pkey_))
|
||||
switch (::EVP_PKEY_id(pkey_))
|
||||
{
|
||||
case EVP_PKEY_RSA:
|
||||
case EVP_PKEY_RSA2:
|
||||
return SSLConfigAPI::PK_RSA;
|
||||
return PKType::PK_RSA;
|
||||
case EVP_PKEY_EC:
|
||||
return SSLConfigAPI::PK_EC;
|
||||
return PKType::PK_EC;
|
||||
case EVP_PKEY_DSA:
|
||||
case EVP_PKEY_DSA1:
|
||||
case EVP_PKEY_DSA2:
|
||||
case EVP_PKEY_DSA3:
|
||||
case EVP_PKEY_DSA4:
|
||||
return SSLConfigAPI::PK_DSA;
|
||||
return PKType::PK_DSA;
|
||||
case EVP_PKEY_NONE:
|
||||
return SSLConfigAPI::PK_NONE;
|
||||
return PKType::PK_NONE;
|
||||
default:
|
||||
return SSLConfigAPI::PK_UNKNOWN;
|
||||
return PKType::PK_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
size_t key_length() const
|
||||
{
|
||||
int ret = i2d_PrivateKey(pkey_, NULL);
|
||||
int ret = ::i2d_PrivateKey(pkey_, NULL);
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
|
||||
@@ -101,12 +129,12 @@ namespace openvpn {
|
||||
|
||||
void parse_pem(const std::string& pkey_txt, const std::string& title)
|
||||
{
|
||||
BIO *bio = BIO_new_mem_buf(const_cast<char *>(pkey_txt.c_str()), pkey_txt.length());
|
||||
BIO *bio = ::BIO_new_mem_buf(const_cast<char *>(pkey_txt.c_str()), pkey_txt.length());
|
||||
if (!bio)
|
||||
throw OpenSSLException();
|
||||
|
||||
EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bio, nullptr, pem_password_callback, this);
|
||||
BIO_free(bio);
|
||||
::EVP_PKEY *pkey = ::PEM_read_bio_PrivateKey(bio, nullptr, pem_password_callback, this);
|
||||
::BIO_free(bio);
|
||||
if (!pkey)
|
||||
throw OpenSSLException(std::string("PKey::parse_pem: error in ") + title + std::string(":"));
|
||||
|
||||
@@ -118,19 +146,19 @@ namespace openvpn {
|
||||
{
|
||||
if (pkey_)
|
||||
{
|
||||
BIO *bio = BIO_new(BIO_s_mem());
|
||||
const int ret = PEM_write_bio_PrivateKey(bio, pkey_, nullptr, nullptr, 0, nullptr, nullptr);
|
||||
BIO *bio = ::BIO_new(BIO_s_mem());
|
||||
const int ret = ::PEM_write_bio_PrivateKey(bio, pkey_, nullptr, nullptr, 0, nullptr, nullptr);
|
||||
if (ret == 0)
|
||||
{
|
||||
BIO_free(bio);
|
||||
::BIO_free(bio);
|
||||
throw OpenSSLException("PKey::render_pem");
|
||||
}
|
||||
|
||||
{
|
||||
char *temp;
|
||||
const int buf_len = BIO_get_mem_data(bio, &temp);
|
||||
const int buf_len = ::BIO_get_mem_data(bio, &temp);
|
||||
std::string ret = std::string(temp, buf_len);
|
||||
BIO_free(bio);
|
||||
::BIO_free(bio);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -138,15 +166,6 @@ namespace openvpn {
|
||||
return "";
|
||||
}
|
||||
|
||||
void erase()
|
||||
{
|
||||
if (pkey_)
|
||||
{
|
||||
EVP_PKEY_free(pkey_);
|
||||
pkey_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
~PKey()
|
||||
{
|
||||
erase();
|
||||
@@ -165,33 +184,31 @@ namespace openvpn {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static EVP_PKEY *dup(const EVP_PKEY *pkey)
|
||||
void erase()
|
||||
{
|
||||
if (pkey_)
|
||||
::EVP_PKEY_free(pkey_);
|
||||
}
|
||||
|
||||
static ::EVP_PKEY *dup(const ::EVP_PKEY *pkey)
|
||||
{
|
||||
// No OpenSSL EVP_PKEY_dup method so we roll our own
|
||||
if (pkey)
|
||||
{
|
||||
EVP_PKEY* pDupKey = EVP_PKEY_new();
|
||||
RSA* pRSA = EVP_PKEY_get1_RSA(const_cast<EVP_PKEY *>(pkey));
|
||||
RSA* pRSADupKey = RSAPrivateKey_dup(pRSA);
|
||||
RSA_free(pRSA);
|
||||
EVP_PKEY_set1_RSA(pDupKey, pRSADupKey);
|
||||
RSA_free(pRSADupKey);
|
||||
::EVP_PKEY* pDupKey = ::EVP_PKEY_new();
|
||||
::RSA* pRSA = ::EVP_PKEY_get1_RSA(const_cast<::EVP_PKEY *>(pkey));
|
||||
::RSA* pRSADupKey = ::RSAPrivateKey_dup(pRSA);
|
||||
::RSA_free(pRSA);
|
||||
::EVP_PKEY_set1_RSA(pDupKey, pRSADupKey);
|
||||
::RSA_free(pRSADupKey);
|
||||
return pDupKey;
|
||||
}
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void assign(const EVP_PKEY *pkey)
|
||||
{
|
||||
erase();
|
||||
pkey_ = dup(pkey);
|
||||
}
|
||||
|
||||
::EVP_PKEY *pkey_;
|
||||
std::string priv_key_pwd;
|
||||
EVP_PKEY *pkey_;
|
||||
};
|
||||
}
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_OPENSSL_PKI_PKEY_H
|
||||
}
|
||||
|
||||
@@ -21,8 +21,7 @@
|
||||
|
||||
// Wrap an OpenSSL X509 object
|
||||
|
||||
#ifndef OPENVPN_OPENSSL_PKI_X509_H
|
||||
#define OPENVPN_OPENSSL_PKI_X509_H
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -32,89 +31,77 @@
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/openssl/util/error.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace OpenSSLPKI {
|
||||
|
||||
class X509;
|
||||
|
||||
class X509Base
|
||||
class X509
|
||||
{
|
||||
public:
|
||||
X509Base() : x509_(nullptr) {}
|
||||
explicit X509Base(::X509 *x509) : x509_(x509) {}
|
||||
X509()
|
||||
: x509_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
X509(const std::string& cert_txt, const std::string& title)
|
||||
: x509_(nullptr)
|
||||
{
|
||||
parse_pem(cert_txt, title);
|
||||
}
|
||||
|
||||
explicit X509(::X509 *x509, const bool create=true)
|
||||
{
|
||||
if (create)
|
||||
x509_ = x509;
|
||||
else
|
||||
x509_ = dup(x509);
|
||||
}
|
||||
|
||||
X509(const X509& other)
|
||||
: x509_(dup(other.x509_))
|
||||
{
|
||||
}
|
||||
|
||||
X509(X509&& other) noexcept
|
||||
: x509_(other.x509_)
|
||||
{
|
||||
other.x509_ = nullptr;
|
||||
}
|
||||
|
||||
X509& operator=(const X509& other)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
erase();
|
||||
x509_ = dup(other.x509_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
X509& operator=(X509&& other) noexcept
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
erase();
|
||||
x509_ = other.x509_;
|
||||
other.x509_ = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool defined() const { return x509_ != nullptr; }
|
||||
::X509* obj() const { return x509_; }
|
||||
::X509* obj_dup() const { return dup(x509_); }
|
||||
|
||||
std::string render_pem() const
|
||||
{
|
||||
if (x509_)
|
||||
{
|
||||
BIO *bio = BIO_new(BIO_s_mem());
|
||||
const int ret = PEM_write_bio_X509(bio, x509_);
|
||||
if (ret == 0)
|
||||
{
|
||||
BIO_free(bio);
|
||||
throw OpenSSLException("X509::render_pem");
|
||||
}
|
||||
|
||||
{
|
||||
char *temp;
|
||||
const int buf_len = BIO_get_mem_data(bio, &temp);
|
||||
std::string ret = std::string(temp, buf_len);
|
||||
BIO_free(bio);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
private:
|
||||
static ::X509 *dup(const ::X509 *x509)
|
||||
{
|
||||
if (x509)
|
||||
return X509_dup(const_cast< ::X509 * >(x509));
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
friend class X509;
|
||||
::X509 *x509_;
|
||||
};
|
||||
|
||||
class X509 : public X509Base, public RC<thread_unsafe_refcount>
|
||||
{
|
||||
public:
|
||||
X509() {}
|
||||
|
||||
X509(const std::string& cert_txt, const std::string& title)
|
||||
{
|
||||
parse_pem(cert_txt, title);
|
||||
}
|
||||
|
||||
X509(const X509& other)
|
||||
{
|
||||
assign(other.x509_);
|
||||
}
|
||||
|
||||
void operator=(const X509& other)
|
||||
{
|
||||
assign(other.x509_);
|
||||
}
|
||||
|
||||
void parse_pem(const std::string& cert_txt, const std::string& title)
|
||||
{
|
||||
BIO *bio = BIO_new_mem_buf(const_cast<char *>(cert_txt.c_str()), cert_txt.length());
|
||||
BIO *bio = ::BIO_new_mem_buf(const_cast<char *>(cert_txt.c_str()), cert_txt.length());
|
||||
if (!bio)
|
||||
throw OpenSSLException();
|
||||
|
||||
::X509 *cert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr);
|
||||
BIO_free(bio);
|
||||
::X509 *cert = ::PEM_read_bio_X509(bio, nullptr, nullptr, nullptr);
|
||||
::BIO_free(bio);
|
||||
if (!cert)
|
||||
throw OpenSSLException(std::string("X509::parse_pem: error in ") + title + std::string(":"));
|
||||
|
||||
@@ -122,13 +109,28 @@ namespace openvpn {
|
||||
x509_ = cert;
|
||||
}
|
||||
|
||||
void erase()
|
||||
std::string render_pem() const
|
||||
{
|
||||
if (x509_)
|
||||
{
|
||||
X509_free(x509_);
|
||||
x509_ = nullptr;
|
||||
BIO *bio = ::BIO_new(BIO_s_mem());
|
||||
const int ret = ::PEM_write_bio_X509(bio, x509_);
|
||||
if (ret == 0)
|
||||
{
|
||||
::BIO_free(bio);
|
||||
throw OpenSSLException("X509::render_pem");
|
||||
}
|
||||
|
||||
{
|
||||
char *temp;
|
||||
const int buf_len = ::BIO_get_mem_data(bio, &temp);
|
||||
std::string ret = std::string(temp, buf_len);
|
||||
::BIO_free(bio);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
~X509()
|
||||
@@ -137,32 +139,40 @@ namespace openvpn {
|
||||
}
|
||||
|
||||
private:
|
||||
void assign(const ::X509 *x509)
|
||||
static ::X509 *dup(const ::X509 *x509)
|
||||
{
|
||||
erase();
|
||||
x509_ = dup(x509);
|
||||
if (x509)
|
||||
return ::X509_dup(const_cast< ::X509 * >(x509));
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void erase()
|
||||
{
|
||||
if (x509_)
|
||||
::X509_free(x509_);
|
||||
}
|
||||
|
||||
::X509 *x509_;
|
||||
};
|
||||
|
||||
typedef RCPtr<X509> X509Ptr;
|
||||
|
||||
class X509List : public std::vector<X509Ptr>
|
||||
class X509List : public std::vector<X509>
|
||||
{
|
||||
public:
|
||||
typedef X509 Item;
|
||||
typedef X509Ptr ItemPtr;
|
||||
|
||||
bool defined() const { return !empty(); }
|
||||
bool defined() const
|
||||
{
|
||||
return !empty();
|
||||
}
|
||||
|
||||
std::string render_pem() const
|
||||
{
|
||||
std::string ret;
|
||||
for (const_iterator i = begin(); i != end(); ++i)
|
||||
ret += (*i)->render_pem();
|
||||
for (const auto &e : *this)
|
||||
ret += e.render_pem();
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
}
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_OPENSSL_PKI_X509_H
|
||||
}
|
||||
|
||||
@@ -21,12 +21,10 @@
|
||||
|
||||
// Wrap an OpenSSL X509Store object
|
||||
|
||||
#ifndef OPENVPN_OPENSSL_PKI_X509STORE_H
|
||||
#define OPENVPN_OPENSSL_PKI_X509STORE_H
|
||||
#pragma once
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/pki/cclist.hpp>
|
||||
#include <openvpn/openssl/util/error.hpp>
|
||||
#include <openvpn/openssl/pki/x509.hpp>
|
||||
@@ -35,16 +33,17 @@
|
||||
namespace openvpn {
|
||||
namespace OpenSSLPKI {
|
||||
|
||||
class X509Store : public RC<thread_unsafe_refcount>
|
||||
class X509Store
|
||||
{
|
||||
public:
|
||||
OPENVPN_SIMPLE_EXCEPTION(x509_store_init_error);
|
||||
OPENVPN_SIMPLE_EXCEPTION(x509_store_add_cert_error);
|
||||
OPENVPN_SIMPLE_EXCEPTION(x509_store_add_crl_error);
|
||||
OPENVPN_EXCEPTION(x509_store_error);
|
||||
|
||||
typedef CertCRLListTemplate<X509List, CRLList> CertCRLList;
|
||||
|
||||
X509Store() : x509_store_(nullptr) {}
|
||||
X509Store()
|
||||
: x509_store_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
explicit X509Store(const CertCRLList& cc)
|
||||
{
|
||||
@@ -52,10 +51,10 @@ namespace openvpn {
|
||||
|
||||
// Load cert list
|
||||
{
|
||||
for (X509List::const_iterator i = cc.certs.begin(); i != cc.certs.end(); ++i)
|
||||
for (const auto &e : cc.certs)
|
||||
{
|
||||
if (!X509_STORE_add_cert(x509_store_, (*i)->obj()))
|
||||
throw x509_store_add_cert_error();
|
||||
if (!::X509_STORE_add_cert(x509_store_, e.obj()))
|
||||
throw x509_store_error("X509_STORE_add_cert(");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,19 +62,22 @@ namespace openvpn {
|
||||
{
|
||||
if (cc.crls.defined())
|
||||
{
|
||||
X509_STORE_set_flags(x509_store_, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
|
||||
for (CRLList::const_iterator i = cc.crls.begin(); i != cc.crls.end(); ++i)
|
||||
::X509_STORE_set_flags(x509_store_, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
|
||||
for (const auto &e : cc.crls)
|
||||
{
|
||||
if (!X509_STORE_add_crl(x509_store_, (*i)->obj()))
|
||||
throw x509_store_add_crl_error();
|
||||
if (!::X509_STORE_add_crl(x509_store_, e.obj()))
|
||||
throw x509_store_error("X509_STORE_add_crl");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
X509_STORE* obj() const { return x509_store_; }
|
||||
X509_STORE* obj() const
|
||||
{
|
||||
return x509_store_;
|
||||
}
|
||||
|
||||
X509_STORE* move()
|
||||
X509_STORE* release()
|
||||
{
|
||||
X509_STORE* ret = x509_store_;
|
||||
x509_store_ = nullptr;
|
||||
@@ -85,20 +87,18 @@ namespace openvpn {
|
||||
~X509Store()
|
||||
{
|
||||
if (x509_store_)
|
||||
X509_STORE_free(x509_store_);
|
||||
::X509_STORE_free(x509_store_);
|
||||
}
|
||||
|
||||
private:
|
||||
void init()
|
||||
{
|
||||
x509_store_ = X509_STORE_new();
|
||||
x509_store_ = ::X509_STORE_new();
|
||||
if (!x509_store_)
|
||||
throw x509_store_init_error();
|
||||
throw x509_store_error("X509_STORE_new");
|
||||
}
|
||||
|
||||
X509_STORE* x509_store_;
|
||||
::X509_STORE* x509_store_;
|
||||
};
|
||||
}
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_OPENSSL_PKI_X509STORE_H
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace openvpn {
|
||||
* On success, return.
|
||||
* On fail, throw exception.
|
||||
*/
|
||||
inline void verify_pkcs7(const OpenSSLPKI::X509Base& cert,
|
||||
inline void verify_pkcs7(const OpenSSLPKI::X509& cert,
|
||||
const std::string& sig,
|
||||
const std::string& data)
|
||||
{
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
#include <openvpn/openssl/pki/x509.hpp>
|
||||
#include <openvpn/openssl/util/error.hpp>
|
||||
|
||||
#include <openvpn/openssl/compat.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
namespace OpenSSLSign {
|
||||
/*
|
||||
@@ -41,21 +43,25 @@ namespace openvpn {
|
||||
* On success, return.
|
||||
* On fail, throw exception.
|
||||
*/
|
||||
inline void verify(const OpenSSLPKI::X509Base& cert,
|
||||
inline void verify(const OpenSSLPKI::X509& cert,
|
||||
const std::string& sig,
|
||||
const std::string& data,
|
||||
const std::string& digest)
|
||||
{
|
||||
const EVP_MD *dig;
|
||||
EVP_MD_CTX md_ctx;
|
||||
bool md_ctx_initialized = false;
|
||||
EVP_MD_CTX* md_ctx = nullptr;
|
||||
EVP_PKEY *pkey = nullptr;
|
||||
|
||||
auto clean = Cleanup([&]() {
|
||||
if (pkey)
|
||||
EVP_PKEY_free(pkey);
|
||||
if (md_ctx_initialized)
|
||||
EVP_MD_CTX_cleanup(&md_ctx);
|
||||
if (md_ctx)
|
||||
{
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
EVP_MD_CTX_cleanup(md_ctx);
|
||||
#endif
|
||||
EVP_MD_CTX_free(md_ctx);
|
||||
}
|
||||
});
|
||||
|
||||
// get digest
|
||||
@@ -78,11 +84,13 @@ namespace openvpn {
|
||||
throw Exception(std::string("OpenSSLSign::verify: base64 decode error on signature: ") + e.what());
|
||||
}
|
||||
|
||||
// initialize digest context
|
||||
md_ctx = EVP_MD_CTX_new();
|
||||
|
||||
// verify signature
|
||||
EVP_VerifyInit (&md_ctx, dig);
|
||||
md_ctx_initialized = 1;
|
||||
EVP_VerifyUpdate(&md_ctx, data.c_str(), data.length());
|
||||
if (EVP_VerifyFinal(&md_ctx, binsig.c_data(), binsig.length(), pkey) != 1)
|
||||
EVP_VerifyInit (md_ctx, dig);
|
||||
EVP_VerifyUpdate(md_ctx, data.c_str(), data.length());
|
||||
if (EVP_VerifyFinal(md_ctx, binsig.c_data(), binsig.length(), pkey) != 1)
|
||||
throw OpenSSLException("OpenSSLSign::verify: verification failed");
|
||||
}
|
||||
}
|
||||
|
||||
+381
-75
@@ -33,6 +33,8 @@
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/x509v3.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/dsa.h>
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/rand.h>
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
@@ -44,6 +46,7 @@
|
||||
#include <openvpn/common/uniqueptr.hpp>
|
||||
#include <openvpn/common/hexstr.hpp>
|
||||
#include <openvpn/common/to_string.hpp>
|
||||
#include <openvpn/common/unicode.hpp>
|
||||
#include <openvpn/frame/frame.hpp>
|
||||
#include <openvpn/buffer/buffer.hpp>
|
||||
#include <openvpn/pki/cclist.hpp>
|
||||
@@ -55,6 +58,7 @@
|
||||
#include <openvpn/ssl/sslconsts.hpp>
|
||||
#include <openvpn/ssl/sslapi.hpp>
|
||||
#include <openvpn/ssl/ssllog.hpp>
|
||||
#include <openvpn/ssl/sni_handler.hpp>
|
||||
#include <openvpn/openssl/util/error.hpp>
|
||||
#include <openvpn/openssl/pki/x509.hpp>
|
||||
#include <openvpn/openssl/pki/crl.hpp>
|
||||
@@ -64,6 +68,10 @@
|
||||
#include <openvpn/openssl/bio/bio_memq_stream.hpp>
|
||||
#include <openvpn/openssl/ssl/sess_cache.hpp>
|
||||
|
||||
#ifdef HAVE_JSON
|
||||
#include <openvpn/common/jsonhelper.hpp>
|
||||
#endif
|
||||
|
||||
// An SSL Context is essentially a configuration that can be used
|
||||
// to generate an arbitrary number of actual SSL connections objects.
|
||||
|
||||
@@ -125,6 +133,18 @@ namespace openvpn {
|
||||
client_session_tickets = v;
|
||||
}
|
||||
|
||||
// server side
|
||||
virtual void set_sni_handler(SNI::HandlerBase* sni_handler_arg)
|
||||
{
|
||||
sni_handler = sni_handler_arg;
|
||||
}
|
||||
|
||||
// client side
|
||||
virtual void set_sni_name(const std::string& sni_name_arg)
|
||||
{
|
||||
sni_name = sni_name_arg;
|
||||
}
|
||||
|
||||
virtual void set_private_key_password(const std::string& pwd)
|
||||
{
|
||||
pkey.set_private_key_password(pwd);
|
||||
@@ -182,7 +202,7 @@ namespace openvpn {
|
||||
std::vector<std::string> ret;
|
||||
|
||||
for (auto const& cert : extra_certs)
|
||||
ret.push_back(cert->render_pem());
|
||||
ret.push_back(cert.render_pem());
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -197,10 +217,10 @@ namespace openvpn {
|
||||
return dh.render_pem();
|
||||
}
|
||||
|
||||
virtual PKType private_key_type() const
|
||||
virtual PKType::Type private_key_type() const
|
||||
{
|
||||
if (!pkey.defined())
|
||||
return PK_NONE;
|
||||
return PKType::PK_NONE;
|
||||
return pkey.key_type();
|
||||
}
|
||||
|
||||
@@ -322,6 +342,13 @@ namespace openvpn {
|
||||
&& opt.exists("client-cert-not-required"))
|
||||
flags |= SSLConst::NO_VERIFY_PEER;
|
||||
|
||||
// sni
|
||||
{
|
||||
const std::string name = opt.get_optional("sni", 1, 256);
|
||||
if (!name.empty())
|
||||
set_sni_name(name);
|
||||
}
|
||||
|
||||
// ca
|
||||
{
|
||||
std::string ca_txt = opt.cat("ca");
|
||||
@@ -379,18 +406,7 @@ namespace openvpn {
|
||||
tls_remote = opt.get_optional(relay_prefix + "tls-remote", 1, 256);
|
||||
|
||||
// Parse tls-version-min option.
|
||||
// Assume that presence of SSL_OP_NO_TLSvX macro indicates
|
||||
// that local OpenSSL library implements TLSvX.
|
||||
{
|
||||
# if defined(SSL_OP_NO_TLSv1_2)
|
||||
const TLSVersion::Type maxver = TLSVersion::V1_2;
|
||||
# elif defined(SSL_OP_NO_TLSv1_1)
|
||||
const TLSVersion::Type maxver = TLSVersion::V1_1;
|
||||
# else
|
||||
const TLSVersion::Type maxver = TLSVersion::V1_0;
|
||||
# endif
|
||||
tls_version_min = TLSVersion::parse_tls_version_min(opt, relay_prefix, maxver);
|
||||
}
|
||||
tls_version_min = TLSVersion::parse_tls_version_min(opt, relay_prefix, maxver());
|
||||
|
||||
// parse tls-cert-profile
|
||||
tls_cert_profile = TLSCertProfile::parse_tls_cert_profile(opt, relay_prefix);
|
||||
@@ -400,7 +416,122 @@ namespace openvpn {
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_JSON
|
||||
virtual SSLConfigAPI::Ptr json_override(const Json::Value& root, const bool load_cert_key) const override
|
||||
{
|
||||
static const char title[] = "json_override";
|
||||
|
||||
Config::Ptr ret(new Config);
|
||||
|
||||
// inherit from self
|
||||
ret->mode = mode;
|
||||
ret->dh = dh;
|
||||
ret->frame = frame;
|
||||
ret->ssl_debug_level = ssl_debug_level;
|
||||
ret->flags = flags;
|
||||
ret->local_cert_enabled = local_cert_enabled;
|
||||
|
||||
// ca
|
||||
{
|
||||
const std::string& ca_txt = json::get_string_ref(root, "ca", title);
|
||||
ret->load_ca(ca_txt, true);
|
||||
}
|
||||
|
||||
// CRL
|
||||
{
|
||||
const std::string crl_txt = json::get_string_optional(root, "crl_verify", std::string(), title);
|
||||
if (!crl_txt.empty())
|
||||
ret->load_crl(crl_txt);
|
||||
}
|
||||
|
||||
// cert/key
|
||||
if (load_cert_key && local_cert_enabled)
|
||||
{
|
||||
bool loaded_cert = false;
|
||||
|
||||
// cert/extra_certs
|
||||
{
|
||||
const std::string cert_txt = json::get_string_optional(root, "cert", std::string(), title);
|
||||
if (!cert_txt.empty())
|
||||
{
|
||||
const std::string ec_txt = json::get_string_optional(root, "extra_certs", std::string(), title);
|
||||
ret->load_cert(cert_txt, ec_txt);
|
||||
loaded_cert = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret->cert = cert;
|
||||
ret->extra_certs = extra_certs;
|
||||
}
|
||||
}
|
||||
|
||||
// private key
|
||||
if (loaded_cert && !external_pki)
|
||||
{
|
||||
const std::string& key_txt = json::get_string_ref(root, "key", title);
|
||||
if (!key_txt.empty())
|
||||
ret->load_private_key(key_txt);
|
||||
else
|
||||
ret->pkey = pkey;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// inherit from self
|
||||
ret->cert = cert;
|
||||
ret->extra_certs = extra_certs;
|
||||
ret->pkey = pkey;
|
||||
}
|
||||
|
||||
// ns_cert_type
|
||||
{
|
||||
const std::string ct = json::get_string_optional(root, "ns_cert_type", std::string(), title);
|
||||
if (!ct.empty())
|
||||
ret->ns_cert_type = NSCert::ns_cert_type(ct);
|
||||
}
|
||||
|
||||
// ku, eku
|
||||
{
|
||||
const std::string ct = json::get_string_optional(root, "remote_cert_tls", std::string(), title);
|
||||
if (!ct.empty())
|
||||
KUParse::remote_cert_tls(ct, ret->ku, ret->eku);
|
||||
}
|
||||
|
||||
// tls_version_min
|
||||
{
|
||||
const std::string tvm = json::get_string_optional(root, "tls_version_min", std::string(), title);
|
||||
if (!tvm.empty())
|
||||
ret->tls_version_min = TLSVersion::parse_tls_version_min(tvm, false, maxver());
|
||||
}
|
||||
|
||||
// tls_cert_profile
|
||||
{
|
||||
const std::string prof = json::get_string_optional(root, "tls_cert_profile", std::string(), title);
|
||||
if (!prof.empty())
|
||||
ret->tls_cert_profile = TLSCertProfile::parse_tls_cert_profile(prof);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
static TLSVersion::Type maxver()
|
||||
{
|
||||
// Return maximum TLS version supported by OpenSSL.
|
||||
// Assume that presence of SSL_OP_NO_TLSvX macro indicates
|
||||
// that local OpenSSL library implements TLSvX.
|
||||
#if defined(SSL_OP_NO_TLSv1_3)
|
||||
return TLSVersion::V1_3;
|
||||
#elif defined(SSL_OP_NO_TLSv1_2)
|
||||
return TLSVersion::V1_2;
|
||||
#elif defined(SSL_OP_NO_TLSv1_1)
|
||||
return TLSVersion::V1_1;
|
||||
#else
|
||||
return TLSVersion::V1_0;
|
||||
#endif
|
||||
}
|
||||
|
||||
Mode mode;
|
||||
CertCRLList ca; // from OpenVPN "ca" and "crl-verify" option
|
||||
OpenSSLPKI::X509 cert; // from OpenVPN "cert" option
|
||||
@@ -409,9 +540,11 @@ namespace openvpn {
|
||||
OpenSSLPKI::DH dh; // diffie-hellman parameters (only needed in server mode)
|
||||
ExternalPKIBase* external_pki = nullptr;
|
||||
TLSSessionTicketBase* session_ticket_handler = nullptr; // server side only
|
||||
SNI::HandlerBase* sni_handler = nullptr; // server side only
|
||||
Frame::Ptr frame;
|
||||
int ssl_debug_level = 0;
|
||||
unsigned int flags = 0; // defined in sslconsts.hpp
|
||||
std::string sni_name; // client side only
|
||||
NSCert::Type ns_cert_type{NSCert::NONE};
|
||||
std::vector<unsigned int> ku; // if defined, peer cert X509 key usage must match one of these values
|
||||
std::string eku; // if defined, peer cert X509 extended key usage must match this OID/string
|
||||
@@ -433,12 +566,12 @@ namespace openvpn {
|
||||
public:
|
||||
typedef RCPtr<SSL> Ptr;
|
||||
|
||||
virtual void start_handshake()
|
||||
void start_handshake() override
|
||||
{
|
||||
SSL_do_handshake(ssl);
|
||||
}
|
||||
|
||||
virtual ssize_t write_cleartext_unbuffered(const void *data, const size_t size)
|
||||
ssize_t write_cleartext_unbuffered(const void *data, const size_t size) override
|
||||
{
|
||||
const int status = BIO_write(ssl_bio, data, size);
|
||||
if (status < 0)
|
||||
@@ -455,7 +588,7 @@ namespace openvpn {
|
||||
return status;
|
||||
}
|
||||
|
||||
virtual ssize_t read_cleartext(void *data, const size_t capacity)
|
||||
ssize_t read_cleartext(void *data, const size_t capacity) override
|
||||
{
|
||||
if (!overflow)
|
||||
{
|
||||
@@ -477,12 +610,12 @@ namespace openvpn {
|
||||
throw ssl_ciphertext_in_overflow();
|
||||
}
|
||||
|
||||
virtual bool read_cleartext_ready() const
|
||||
bool read_cleartext_ready() const override
|
||||
{
|
||||
return !bmq_stream::memq_from_bio(ct_in)->empty() || SSL_pending(ssl) > 0;
|
||||
}
|
||||
|
||||
virtual void write_ciphertext(const BufferPtr& buf)
|
||||
void write_ciphertext(const BufferPtr& buf) override
|
||||
{
|
||||
bmq_stream::MemQ* in = bmq_stream::memq_from_bio(ct_in);
|
||||
if (in->size() < MAX_CIPHERTEXT_IN)
|
||||
@@ -491,7 +624,7 @@ namespace openvpn {
|
||||
overflow = true;
|
||||
}
|
||||
|
||||
virtual void write_ciphertext_unbuffered(const unsigned char *data, const size_t size)
|
||||
void write_ciphertext_unbuffered(const unsigned char *data, const size_t size) override
|
||||
{
|
||||
bmq_stream::MemQ* in = bmq_stream::memq_from_bio(ct_in);
|
||||
if (in->size() < MAX_CIPHERTEXT_IN)
|
||||
@@ -500,17 +633,17 @@ namespace openvpn {
|
||||
overflow = true;
|
||||
}
|
||||
|
||||
virtual bool read_ciphertext_ready() const
|
||||
bool read_ciphertext_ready() const override
|
||||
{
|
||||
return !bmq_stream::memq_from_bio(ct_out)->empty();
|
||||
}
|
||||
|
||||
virtual BufferPtr read_ciphertext()
|
||||
BufferPtr read_ciphertext() override
|
||||
{
|
||||
return bmq_stream::memq_from_bio(ct_out)->read_buf();
|
||||
}
|
||||
|
||||
virtual std::string ssl_handshake_details() const
|
||||
std::string ssl_handshake_details() const override
|
||||
{
|
||||
return ssl_handshake_details(ssl);
|
||||
}
|
||||
@@ -526,7 +659,7 @@ namespace openvpn {
|
||||
return !SSL_session_reused(ssl);
|
||||
}
|
||||
|
||||
virtual const AuthCert::Ptr& auth_cert()
|
||||
const AuthCert::Ptr& auth_cert() const override
|
||||
{
|
||||
// Reused sessions don't call the cert verify callbacks,
|
||||
// so we must use an alternative method to build authcert.
|
||||
@@ -535,7 +668,7 @@ namespace openvpn {
|
||||
return authcert;
|
||||
}
|
||||
|
||||
virtual void mark_no_cache()
|
||||
void mark_no_cache() override
|
||||
{
|
||||
sess_cache_key.reset();
|
||||
}
|
||||
@@ -549,7 +682,7 @@ namespace openvpn {
|
||||
{
|
||||
bmq_stream::init_static();
|
||||
|
||||
mydata_index = SSL_get_ex_new_index(0, (char *)"OpenSSLContext::SSL", nullptr, nullptr, nullptr);
|
||||
ssl_data_index = SSL_get_ex_new_index(0, (char *)"OpenSSLContext::SSL", nullptr, nullptr, nullptr);
|
||||
context_data_index = SSL_get_ex_new_index(0, (char *)"OpenSSLContext", nullptr, nullptr, nullptr);
|
||||
|
||||
/*
|
||||
@@ -592,7 +725,7 @@ namespace openvpn {
|
||||
SSL_set_mode(ssl, SSL_MODE_RELEASE_BUFFERS);
|
||||
|
||||
// verify hostname
|
||||
if (hostname)
|
||||
if (hostname && !(ctx.config->flags & SSLConst::NO_VERIFY_HOSTNAME))
|
||||
{
|
||||
X509_VERIFY_PARAM *param = SSL_get0_param(ssl);
|
||||
X509_VERIFY_PARAM_set_hostflags(param, 0);
|
||||
@@ -628,9 +761,18 @@ namespace openvpn {
|
||||
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)
|
||||
throw OpenSSLException("OpenSSLContext::SSL: SSL_set_tlsext_host_name failed");
|
||||
|
||||
// client-side SNI
|
||||
if (!ctx.config->sni_name.empty())
|
||||
{
|
||||
if (SSL_set_tlsext_host_name(ssl, ctx.config->sni_name.c_str()) != 1)
|
||||
throw OpenSSLException("OpenSSLContext::SSL: SSL_set_tlsext_host_name failed (sni_name)");
|
||||
}
|
||||
else if ((ctx.config->flags & SSLConst::ENABLE_CLIENT_SNI) && hostname)
|
||||
{
|
||||
if (SSL_set_tlsext_host_name(ssl, hostname->c_str()) != 1)
|
||||
throw OpenSSLException("OpenSSLContext::SSL: SSL_set_tlsext_host_name failed (hostname)");
|
||||
}
|
||||
}
|
||||
else
|
||||
OPENVPN_THROW(ssl_context_error, "OpenSSLContext::SSL: unknown client/server mode");
|
||||
@@ -640,12 +782,10 @@ namespace openvpn {
|
||||
SSL_set_bio (ssl, ct_in, ct_out);
|
||||
BIO_set_ssl (ssl_bio, ssl, BIO_NOCLOSE);
|
||||
|
||||
if (mydata_index < 0)
|
||||
throw ssl_context_error("OpenSSLContext::SSL: mydata_index is uninitialized");
|
||||
if (context_data_index < 0)
|
||||
throw ssl_context_error("OpenSSLContext::SSL: context_data_index is uninitialized");
|
||||
SSL_set_ex_data (ssl, mydata_index, this);
|
||||
SSL_set_ex_data (ssl, context_data_index, (void*) &ctx);
|
||||
if (ssl_data_index < 0)
|
||||
throw ssl_context_error("OpenSSLContext::SSL: ssl_data_index is uninitialized");
|
||||
SSL_set_ex_data (ssl, ssl_data_index, this);
|
||||
set_parent(&ctx);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@@ -654,9 +794,15 @@ namespace openvpn {
|
||||
}
|
||||
}
|
||||
|
||||
void rebuild_authcert()
|
||||
void set_parent(const OpenSSLContext* ctx)
|
||||
{
|
||||
if (context_data_index < 0)
|
||||
throw ssl_context_error("OpenSSLContext::SSL: context_data_index is uninitialized");
|
||||
SSL_set_ex_data(ssl, context_data_index, (void *)ctx);
|
||||
}
|
||||
|
||||
void rebuild_authcert() const
|
||||
{
|
||||
authcert.reset(new AuthCert());
|
||||
::X509 *cert = SSL_get_peer_certificate(ssl);
|
||||
if (cert)
|
||||
{
|
||||
@@ -684,17 +830,21 @@ namespace openvpn {
|
||||
}
|
||||
|
||||
// Print a one line summary of SSL/TLS session handshake.
|
||||
static std::string ssl_handshake_details (::SSL *c_ssl)
|
||||
static std::string ssl_handshake_details (const ::SSL *c_ssl)
|
||||
{
|
||||
std::ostringstream os;
|
||||
|
||||
::X509 *cert = SSL_get_peer_certificate (c_ssl);
|
||||
|
||||
if (cert)
|
||||
os << "CN=" << x509_get_field(cert, NID_commonName) << ", ";
|
||||
|
||||
os << SSL_get_version (c_ssl);
|
||||
|
||||
const SSL_CIPHER *ciph = SSL_get_current_cipher (c_ssl);
|
||||
if (ciph)
|
||||
os << ", cipher " << SSL_CIPHER_get_version (ciph) << ' ' << SSL_CIPHER_get_name (ciph);
|
||||
|
||||
::X509 *cert = SSL_get_peer_certificate (c_ssl);
|
||||
if (cert != nullptr)
|
||||
{
|
||||
EVP_PKEY *pkey = X509_get_pubkey (cert);
|
||||
@@ -710,7 +860,10 @@ namespace openvpn {
|
||||
}
|
||||
X509_free (cert);
|
||||
}
|
||||
if (SSL_session_reused(c_ssl))
|
||||
// This has been changed in upstream SSL to have a const
|
||||
// parameter, so we cast away const for older versions compatibility
|
||||
// (Upstream commit: c04b66b18d1a90f0c6326858e4b8367be5444582)
|
||||
if (SSL_session_reused(const_cast<::SSL *>(c_ssl)))
|
||||
os << " [REUSED]";
|
||||
return os.str();
|
||||
}
|
||||
@@ -792,12 +945,13 @@ namespace openvpn {
|
||||
BIO *ct_out; // read ciphertext from here
|
||||
AuthCert::Ptr authcert;
|
||||
OpenSSLSessionCache::Key::UPtr sess_cache_key; // client-side only
|
||||
OpenSSLContext::Ptr sni_ctx;
|
||||
bool ssl_bio_linkage;
|
||||
bool overflow;
|
||||
bool called_did_full_handshake;
|
||||
|
||||
// Helps us to store pointer to self in ::SSL object
|
||||
static int mydata_index;
|
||||
static int ssl_data_index;
|
||||
static int context_data_index;
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
@@ -1003,6 +1157,17 @@ namespace openvpn {
|
||||
throw OpenSSLException("OpenSSLContext: SSL_CTX_set_tmp_dh failed");
|
||||
if (config->flags & SSLConst::SERVER_TO_SERVER)
|
||||
SSL_CTX_set_purpose(ctx, X509_PURPOSE_SSL_SERVER);
|
||||
|
||||
// server-side SNI
|
||||
if (config->sni_handler)
|
||||
{
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10101000L
|
||||
#define OPENSSL_SERVER_SNI
|
||||
SSL_CTX_set_client_hello_cb(ctx, client_hello_callback, nullptr);
|
||||
#else
|
||||
OPENVPN_THROW(ssl_context_error, "OpenSSLContext: server-side SNI requires OpenSSL 1.1 or higher");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else if (config->mode.is_client())
|
||||
{
|
||||
@@ -1024,11 +1189,8 @@ namespace openvpn {
|
||||
SSL_CTX_set_verify_depth(ctx, 16);
|
||||
}
|
||||
|
||||
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;
|
||||
long sslopt = SSL_OP_SINGLE_DH_USE | SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_COMPRESSION | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
|
||||
|
||||
if (config->mode.is_server())
|
||||
{
|
||||
@@ -1044,6 +1206,16 @@ namespace openvpn {
|
||||
}
|
||||
else
|
||||
sslopt |= SSL_OP_NO_TICKET;
|
||||
|
||||
// send a client CA list to the client
|
||||
if (config->flags & SSLConst::SEND_CLIENT_CA_LIST)
|
||||
{
|
||||
for (const auto& e : config->ca.certs)
|
||||
{
|
||||
if (SSL_CTX_add_client_CA(ctx, e.obj()) != 1)
|
||||
throw OpenSSLException("OpenSSLContext: SSL_CTX_add_client_CA failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1062,23 +1234,20 @@ namespace openvpn {
|
||||
/* mbed TLS also ignores tls version when force aes cbc cipher suites is on */
|
||||
if (!config->force_aes_cbc_ciphersuites)
|
||||
{
|
||||
if (!config->force_aes_cbc_ciphersuites || config->tls_version_min > TLSVersion::UNDEF)
|
||||
{
|
||||
if (config->tls_version_min > TLSVersion::V1_0)
|
||||
sslopt |= SSL_OP_NO_TLSv1;
|
||||
# ifdef SSL_OP_NO_TLSv1_1
|
||||
if (config->tls_version_min > TLSVersion::V1_1)
|
||||
sslopt |= SSL_OP_NO_TLSv1_1;
|
||||
# endif
|
||||
# ifdef SSL_OP_NO_TLSv1_2
|
||||
if (config->tls_version_min > TLSVersion::V1_2)
|
||||
sslopt |= SSL_OP_NO_TLSv1_2;
|
||||
# endif
|
||||
# ifdef SSL_OP_NO_TLSv1_3
|
||||
if (config->tls_version_min > TLSVersion::V1_3)
|
||||
sslopt |= SSL_OP_NO_TLSv1_3;
|
||||
# endif
|
||||
}
|
||||
if (config->tls_version_min > TLSVersion::V1_0)
|
||||
sslopt |= SSL_OP_NO_TLSv1;
|
||||
# ifdef SSL_OP_NO_TLSv1_1
|
||||
if (config->tls_version_min > TLSVersion::V1_1)
|
||||
sslopt |= SSL_OP_NO_TLSv1_1;
|
||||
# endif
|
||||
# ifdef SSL_OP_NO_TLSv1_2
|
||||
if (config->tls_version_min > TLSVersion::V1_2)
|
||||
sslopt |= SSL_OP_NO_TLSv1_2;
|
||||
# endif
|
||||
# ifdef SSL_OP_NO_TLSv1_3
|
||||
if (config->tls_version_min > TLSVersion::V1_3)
|
||||
sslopt |= SSL_OP_NO_TLSv1_3;
|
||||
# endif
|
||||
}
|
||||
SSL_CTX_set_options(ctx, sslopt);
|
||||
|
||||
@@ -1178,9 +1347,9 @@ namespace openvpn {
|
||||
// chain but shouldn't be included in the verify chain.
|
||||
if (config->extra_certs.defined())
|
||||
{
|
||||
for (OpenSSLPKI::X509List::const_iterator i = config->extra_certs.begin(); i != config->extra_certs.end(); ++i)
|
||||
for (const auto& e : config->extra_certs)
|
||||
{
|
||||
if (SSL_CTX_add_extra_chain_cert(ctx, (*i)->obj_dup()) != 1)
|
||||
if (SSL_CTX_add_extra_chain_cert(ctx, e.obj_dup()) != 1)
|
||||
throw OpenSSLException("OpenSSLContext: SSL_CTX_add_extra_chain_cert failed");
|
||||
}
|
||||
}
|
||||
@@ -1219,7 +1388,7 @@ namespace openvpn {
|
||||
void update_trust(const CertCRLList& cc)
|
||||
{
|
||||
OpenSSLPKI::X509Store store(cc);
|
||||
SSL_CTX_set_cert_store(ctx, store.move());
|
||||
SSL_CTX_set_cert_store(ctx, store.release());
|
||||
}
|
||||
|
||||
~OpenSSLContext()
|
||||
@@ -1528,7 +1697,7 @@ namespace openvpn {
|
||||
case X509_V_ERR_CERT_HAS_EXPIRED:
|
||||
return AuthCert::Fail::EXPIRED;
|
||||
default:
|
||||
return AuthCert::Fail::OTHER;
|
||||
return AuthCert::Fail::CERT_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1601,7 +1770,7 @@ namespace openvpn {
|
||||
const OpenSSLContext* self = (OpenSSLContext*) SSL_get_ex_data (ssl, SSL::context_data_index);
|
||||
|
||||
// get OpenSSLContext::SSL
|
||||
SSL* self_ssl = (SSL *) SSL_get_ex_data (ssl, SSL::mydata_index);
|
||||
SSL* self_ssl = (SSL *) SSL_get_ex_data (ssl, SSL::ssl_data_index);
|
||||
|
||||
// get error code
|
||||
const int err = X509_STORE_CTX_get_error(ctx);
|
||||
@@ -1678,9 +1847,7 @@ namespace openvpn {
|
||||
self->config->x509_track_config,
|
||||
*self_ssl->authcert->x509_track);
|
||||
|
||||
return preverify_ok || ((self->config->flags & SSLConst::DEFERRED_CERT_VERIFY)
|
||||
&& self_ssl->authcert // failsafe: don't defer error unless
|
||||
&& self_ssl->authcert->is_fail()); // authcert has recorded it
|
||||
return preverify_ok || self->deferred_cert_verify_failsafe(*self_ssl);
|
||||
}
|
||||
|
||||
// Print debugging information on SSL/TLS session negotiation.
|
||||
@@ -1806,6 +1973,139 @@ namespace openvpn {
|
||||
return true;
|
||||
}
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10101000L
|
||||
|
||||
static int client_hello_callback(::SSL *s, int *al, void *)
|
||||
{
|
||||
std::string sni_name;
|
||||
|
||||
// get OpenSSLContext
|
||||
OpenSSLContext* self = (OpenSSLContext*) SSL_get_ex_data(s, SSL::context_data_index);
|
||||
|
||||
// get OpenSSLContext::SSL
|
||||
SSL* self_ssl = (SSL *) SSL_get_ex_data(s, SSL::ssl_data_index);
|
||||
|
||||
try {
|
||||
// get the SNI from the client hello
|
||||
sni_name = client_hello_get_sni(s);
|
||||
|
||||
// process the SNI name, if provided
|
||||
if (!sni_name.empty())
|
||||
{
|
||||
// save the SNI name in authcert
|
||||
if (self_ssl->authcert)
|
||||
self_ssl->authcert->sni = sni_name;
|
||||
|
||||
// ignore the SNI if no handler was provided
|
||||
if (self->config->sni_handler)
|
||||
{
|
||||
// get an alternative SSLFactoryAPI from the sni_handler
|
||||
SSLFactoryAPI::Ptr fapi;
|
||||
try {
|
||||
SNI::Metadata::UPtr sm;
|
||||
fapi = self->config->sni_handler->sni_hello(sni_name, sm, self->config);
|
||||
if (self_ssl->authcert)
|
||||
self_ssl->authcert->sni_metadata = std::move(sm);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
OPENVPN_LOG("SNI HANDLER ERROR: " << e.what());
|
||||
return sni_error(e.what(), SSL_AD_INTERNAL_ERROR, self, self_ssl, al);
|
||||
}
|
||||
if (!fapi)
|
||||
return sni_error("SNI name not found", SSL_AD_UNRECOGNIZED_NAME, self, self_ssl, al);
|
||||
|
||||
// make sure that returned SSLFactoryAPI is an OpenSSLContext
|
||||
self_ssl->sni_ctx = fapi.dynamic_pointer_cast<OpenSSLContext>();
|
||||
if (!self_ssl->sni_ctx)
|
||||
throw Exception("sni_handler returned wrong kind of SSLFactoryAPI");
|
||||
|
||||
// don't modify SSL CTX if the returned SSLFactoryAPI is ourself
|
||||
if (fapi.get() != self)
|
||||
{
|
||||
SSL_set_SSL_CTX(s, self_ssl->sni_ctx->ctx);
|
||||
self_ssl->set_parent(self_ssl->sni_ctx.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
return SSL_CLIENT_HELLO_SUCCESS;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
OPENVPN_LOG("SNI exception in OpenSSLContext, SNI=" << sni_name << " : " << e.what());
|
||||
*al = SSL_AD_INTERNAL_ERROR;
|
||||
return SSL_CLIENT_HELLO_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
static int sni_error(std::string err,
|
||||
const int ssl_ad_error,
|
||||
OpenSSLContext* self,
|
||||
SSL* self_ssl,
|
||||
int* al)
|
||||
{
|
||||
if (self_ssl->authcert)
|
||||
self_ssl->authcert->add_fail(0, AuthCert::Fail::SNI_ERROR, std::move(err));
|
||||
if (self->deferred_cert_verify_failsafe(*self_ssl))
|
||||
return SSL_CLIENT_HELLO_SUCCESS;
|
||||
*al = ssl_ad_error;
|
||||
return SSL_CLIENT_HELLO_ERROR;
|
||||
}
|
||||
|
||||
static size_t sni_get_len(ConstBuffer& buf)
|
||||
{
|
||||
size_t ret = buf.pop_front() << 8;
|
||||
ret += buf.pop_front();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static std::string client_hello_get_sni(::SSL* s)
|
||||
{
|
||||
const unsigned char *p;
|
||||
size_t remaining;
|
||||
if (!SSL_client_hello_get0_ext(s, TLSEXT_TYPE_server_name, &p, &remaining))
|
||||
return std::string();
|
||||
|
||||
// For safety, map a ConstBuffer onto returned OpenSSL TLSEXT_TYPE_server_name data.
|
||||
ConstBuffer buf(p, remaining, true);
|
||||
|
||||
// Extract the length of the supplied list of names,
|
||||
// and check that it matches size of remaining data
|
||||
// in buf.
|
||||
{
|
||||
const size_t len = sni_get_len(buf);
|
||||
if (len != buf.size())
|
||||
throw Exception("bad name list size");
|
||||
}
|
||||
|
||||
// Next byte must be TLSEXT_NAMETYPE_host_name.
|
||||
if (buf.pop_front() != TLSEXT_NAMETYPE_host_name)
|
||||
throw Exception("expecting TLSEXT_NAMETYPE_host_name");
|
||||
|
||||
// Now try to extract the SNI name.
|
||||
{
|
||||
const size_t len = sni_get_len(buf);
|
||||
if (len > buf.size())
|
||||
throw Exception("bad name size");
|
||||
if (!Unicode::is_valid_utf8_uchar_buf(buf.c_data(), len, 1024 | Unicode::UTF8_NO_CTRL))
|
||||
throw Exception("invalid UTF-8");
|
||||
return std::string((const char *)buf.c_data(), len);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Return true if we should continue with authentication
|
||||
// even though there was an error, because the user has
|
||||
// enabled SSLConst::DEFERRED_CERT_VERIFY and wants the
|
||||
// error to be logged in authcert so that it can be handled
|
||||
// by a higher layer.
|
||||
bool deferred_cert_verify_failsafe(const SSL& ssl) const
|
||||
{
|
||||
return (config->flags & SSLConst::DEFERRED_CERT_VERIFY)
|
||||
&& ssl.authcert // failsafe: don't defer error unless
|
||||
&& ssl.authcert->is_fail(); // authcert has recorded it
|
||||
}
|
||||
|
||||
void erase()
|
||||
{
|
||||
if (epki)
|
||||
@@ -1826,13 +2126,19 @@ namespace openvpn {
|
||||
OpenSSLSessionCache::Ptr sess_cache; // client-side only
|
||||
};
|
||||
|
||||
int OpenSSLContext::SSL::mydata_index = -1;
|
||||
#ifdef OPENVPN_NO_EXTERN
|
||||
int OpenSSLContext::SSL::ssl_data_index = -1;
|
||||
int OpenSSLContext::SSL::context_data_index = -1;
|
||||
#endif
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L && defined(OPENVPN_NO_EXTERN)
|
||||
SSL_METHOD OpenSSLContext::SSL::ssl23_method_client_;
|
||||
SSL_METHOD OpenSSLContext::SSL::ssl23_method_server_;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline const std::string get_ssl_library_version()
|
||||
{
|
||||
return OPENSSL_VERSION_TEXT;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
// 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
|
||||
|
||||
// seed OpenSSL's random number generator with /dev/urandom
|
||||
|
||||
#include <openssl/rand.h>
|
||||
|
||||
#include <openvpn/random/devurand.hpp>
|
||||
|
||||
namespace openvpn {
|
||||
inline void openssl_reseed_rng()
|
||||
{
|
||||
unsigned char entropy[64];
|
||||
|
||||
RandomAPI::Ptr rng(new DevURand);
|
||||
rng->rand_bytes(entropy, sizeof(entropy));
|
||||
|
||||
RAND_seed(entropy, sizeof(entropy));
|
||||
}
|
||||
}
|
||||
@@ -66,15 +66,17 @@ namespace openvpn {
|
||||
TokenEncrypt(const Key& key, const int mode)
|
||||
{
|
||||
ctx = EVP_CIPHER_CTX_new ();
|
||||
EVP_CIPHER_CTX_init (ctx);
|
||||
EVP_CIPHER_CTX_reset (ctx);
|
||||
if (!EVP_CipherInit_ex(ctx, EVP_aes_128_ecb(), nullptr, key.data, nullptr, mode))
|
||||
throw OpenSSLException("TokenEncrypt: EVP_CipherInit_ex[1] failed");
|
||||
{
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
throw OpenSSLException("TokenEncrypt: EVP_CipherInit_ex[1] failed");
|
||||
}
|
||||
EVP_CIPHER_CTX_set_padding(ctx, 0);
|
||||
}
|
||||
|
||||
~TokenEncrypt()
|
||||
{
|
||||
EVP_CIPHER_CTX_cleanup(ctx);
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user