Merge commit '86cc97e55fe346502462284d2e636a2b3708163e' as 'Sources/OpenVPN3'

This commit is contained in:
Sergey Abramchuk
2020-02-24 14:43:11 +03:00
655 changed files with 146468 additions and 0 deletions

View File

@@ -0,0 +1,61 @@
// 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/>.
// Method to set up a particular OpenSSL engine type
#ifndef OPENVPN_OPENSSL_UTIL_ENGINE_H
#define OPENVPN_OPENSSL_UTIL_ENGINE_H
#include <string>
#ifndef OPENSSL_NO_ENGINE
#include <openssl/engine.h>
#endif
#include <openvpn/common/exception.hpp>
#include <openvpn/openssl/util/error.hpp>
namespace openvpn {
OPENVPN_EXCEPTION(openssl_engine_error);
inline void openssl_setup_engine (const std::string& engine)
{
#ifndef OPENSSL_NO_ENGINE
ENGINE_load_builtin_engines ();
if (engine == "auto")
{
ENGINE_register_all_complete ();
return;
}
ENGINE *e = ENGINE_by_id (engine.c_str());
if (!e)
throw openssl_engine_error();
if (!ENGINE_set_default (e, ENGINE_METHOD_ALL))
throw openssl_engine_error();
#endif
}
} // namespace openvpn
#endif // OPENVPN_OPENSSL_UTIL_ENGINE_H

View File

@@ -0,0 +1,200 @@
// 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/>.
// OpenSSL exception class that allows a full OpenSSL error stack
// to be represented.
#ifndef OPENVPN_OPENSSL_UTIL_ERROR_H
#define OPENVPN_OPENSSL_UTIL_ERROR_H
#include <string>
#include <openssl/err.h>
#include <openssl/ssl.h>
#include <openvpn/common/exception.hpp>
#include <openvpn/error/error.hpp>
#include <openvpn/error/excode.hpp>
namespace openvpn {
// string exception class
class OpenSSLException : public ExceptionCode
{
public:
OPENVPN_EXCEPTION(ssl_exception_index);
enum {
MAX_ERRORS = 8
};
OpenSSLException()
{
ssl_err = -1;
init_error("OpenSSL");
}
explicit OpenSSLException(const std::string& error_text)
{
ssl_err = -1;
init_error(error_text.c_str());
}
explicit OpenSSLException(const int ssl_error)
{
init_ssl_error(ssl_error, "OpenSSL");
}
explicit OpenSSLException(const std::string& error_text, const int ssl_error)
{
init_ssl_error(ssl_error, error_text.c_str());
}
virtual const char* what() const throw() { return errtxt.c_str(); }
std::string what_str() const { return errtxt; }
size_t len() const { return n_err; }
unsigned long operator[](const size_t i) const
{
if (i < n_err)
return errstack[i];
else
throw ssl_exception_index();
}
int ssl_error() const { return ssl_err; }
virtual ~OpenSSLException() throw() {}
static const char *ssl_error_text(const int ssl_error, bool *unknown = nullptr)
{
switch (ssl_error)
{
case SSL_ERROR_NONE:
return "SSL_ERROR_NONE";
case SSL_ERROR_ZERO_RETURN:
return "SSL_ERROR_ZERO_RETURN";
case SSL_ERROR_WANT_READ:
return "SSL_ERROR_WANT_READ";
case SSL_ERROR_WANT_WRITE:
return "SSL_ERROR_WANT_WRITE";
case SSL_ERROR_WANT_CONNECT:
return "SSL_ERROR_WANT_CONNECT";
case SSL_ERROR_WANT_ACCEPT:
return "SSL_ERROR_WANT_ACCEPT";
case SSL_ERROR_WANT_X509_LOOKUP:
return "SSL_ERROR_WANT_X509_LOOKUP";
case SSL_ERROR_SYSCALL:
return "SSL_ERROR_SYSCALL";
case SSL_ERROR_SSL:
return "SSL_ERROR_SSL";
default:
if (unknown)
*unknown = true;
return "(unknown SSL error)";
}
}
private:
void init_error(const char *error_text)
{
const char *prefix = ": ";
std::ostringstream tmp;
char buf[256];
tmp << error_text;
n_err = 0;
while (unsigned long err = ERR_get_error())
{
if (n_err < MAX_ERRORS)
errstack[n_err++] = err;
ERR_error_string_n(err, buf, sizeof(buf));
tmp << prefix << buf;
prefix = " / ";
// for certain OpenSSL errors, translate them to an OpenVPN error code,
// so they can be propagated up to the higher levels (such as UI level)
switch (ERR_GET_REASON(err))
{
case SSL_R_CERTIFICATE_VERIFY_FAILED:
set_code(Error::CERT_VERIFY_FAIL, true);
break;
case PEM_R_BAD_PASSWORD_READ:
case PEM_R_BAD_DECRYPT:
set_code(Error::PEM_PASSWORD_FAIL, true);
break;
case SSL_R_UNSUPPORTED_PROTOCOL:
set_code(Error::TLS_VERSION_MIN, true);
break;
}
}
errtxt = tmp.str();
}
void init_ssl_error(const int ssl_error, const char *error_text)
{
bool unknown = false;
ssl_err = ssl_error;
const char *text = ssl_error_text(ssl_error, &unknown);
if (unknown || ssl_error == SSL_ERROR_SYSCALL || ssl_error == SSL_ERROR_SSL)
{
init_error(error_text);
errtxt += " (";
errtxt += text;
errtxt += ")";
}
else
{
errtxt = error_text;
errtxt += ": ";
errtxt += text;
}
}
size_t n_err;
unsigned long errstack[MAX_ERRORS];
std::string errtxt;
int ssl_err;
};
// return an OpenSSL error string
inline std::string openssl_error()
{
OpenSSLException err;
return err.what_str();
}
inline std::string openssl_error(const int ssl_error)
{
OpenSSLException err(ssl_error);
return err.what_str();
}
inline void openssl_clear_error_stack()
{
while (ERR_get_error())
;
}
} // namespace openvpn
#endif // OPENVPN_OPENSSL_UTIL_ERROR_H

View File

@@ -0,0 +1,39 @@
// 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 <openssl/opensslv.h>
// OpenSSL 1.1.0 does not require an explicit init, in fact the
// asio init for 1.1.0 is a noop, see also OPENSSL_init_ssl man page
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
#define OPENSSL_NEEDS_INIT
// Instantiate this object to ensure openssl is initialised.
#ifdef USE_ASIO
#include <asio/ssl/detail/openssl_init.hpp>
typedef asio::ssl::detail::openssl_init<> openssl_init;
#else
#error no OpenSSL init code (USE_ASIO needed for OpenSSL < 1.1)
#endif
#endif

View File

@@ -0,0 +1,103 @@
// 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) 2017-2018 OpenVPN Technologies, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
// Wrap the OpenSSL PEM API defined in <openssl/pem.h> so
// that it can be used as part of the crypto layer of the OpenVPN core.
#ifndef OPENVPN_OPENSSL_UTIL_PEM_H
#define OPENVPN_OPENSSL_UTIL_PEM_H
#include <openvpn/openssl/util/error.hpp>
#include <openssl/pem.h>
namespace openvpn {
class OpenSSLPEM
{
public:
static bool pem_encode(BufferAllocated& dst, const unsigned char *src,
size_t src_len, const std::string& key_name)
{
bool ret = false;
BIO *bio = BIO_new(BIO_s_mem());
if (!bio)
return false;
if (!PEM_write_bio(bio, key_name.c_str(), "", src, src_len))
goto out;
BUF_MEM *bptr;
BIO_get_mem_ptr(bio, &bptr);
dst.write((unsigned char *)bptr->data, bptr->length);
ret = true;
out:
if (!BIO_free(bio))
ret = false;
return ret;
}
static bool pem_decode(BufferAllocated& dst, const char *src,
size_t src_len, const std::string& key_name)
{
bool ret = false;
BIO *bio;
if (!(bio = BIO_new_mem_buf(src, src_len)))
throw OpenSSLException("Cannot open memory BIO for PEM decode");
char *name_read = NULL;
char *header_read = NULL;
uint8_t *data_read = NULL;
long data_read_len = 0;
if (!PEM_read_bio(bio, &name_read, &header_read, &data_read,
&data_read_len))
{
OPENVPN_LOG("PEM decode failed");
goto out;
}
if (key_name.compare(std::string(name_read)))
{
OPENVPN_LOG("unexpected PEM name (got '" << name_read <<
"', expected '" << key_name << "')");
goto out;
}
dst.write(data_read, data_read_len);
ret = true;
out:
OPENSSL_free(name_read);
OPENSSL_free(header_read);
OPENSSL_free(data_read);
if (!BIO_free(bio))
ret = false;
return ret;
}
};
};
#endif /* OPENVPN_OPENSSL_UTIL_PEM_H */

View File

@@ -0,0 +1,78 @@
// 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/>.
// Wrap the OpenSSL Cryptographic Random API defined in <openssl/rand.h>
// so that it can be used as the primary source of cryptographic entropy by
// the OpenVPN core.
#ifndef OPENVPN_OPENSSL_UTIL_RAND_H
#define OPENVPN_OPENSSL_UTIL_RAND_H
#include <openssl/rand.h>
#include <openvpn/random/randapi.hpp>
namespace openvpn {
class OpenSSLRandom : public RandomAPI
{
public:
OPENVPN_EXCEPTION(rand_error_openssl);
typedef RCPtr<OpenSSLRandom> Ptr;
OpenSSLRandom(const bool prng)
{
}
virtual std::string name() const
{
return "OpenSSLRandom";
}
// Return true if algorithm is crypto-strength
virtual bool is_crypto() const
{
return true;
}
// Fill buffer with random bytes
virtual void rand_bytes(unsigned char *buf, size_t size)
{
if (!rndbytes(buf, size))
throw rand_error_openssl("rand_bytes");
}
// Like rand_bytes, but don't throw exception.
// Return true on successs, false on fail.
virtual bool rand_bytes_noexcept(unsigned char *buf, size_t size)
{
return rndbytes(buf, size);
}
private:
bool rndbytes(unsigned char *buf, size_t size)
{
return RAND_bytes(buf, size) == 1;
}
};
}
#endif

View File

@@ -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));
}
}

View File

@@ -0,0 +1,121 @@
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2017 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
#ifndef OPENVPN_CRYPTO_TOKENENCRYPT_H
#define OPENVPN_CRYPTO_TOKENENCRYPT_H
#include <string>
#include <atomic>
#include <cstdint> // for std::uint8_t
#include <openssl/evp.h>
#include <openvpn/common/size.hpp>
#include <openvpn/common/exception.hpp>
#include <openvpn/common/base64.hpp>
#include <openvpn/buffer/buffer.hpp>
#include <openvpn/random/randapi.hpp>
#include <openvpn/openssl/util/error.hpp>
#include <openvpn/openssl/compat.hpp>
namespace openvpn {
class TokenEncrypt
{
public:
class Key
{
public:
static constexpr size_t SIZE = 16;
Key(RandomAPI& rng)
{
rng.assert_crypto();
rng.rand_bytes(data, sizeof(data));
}
private:
friend class TokenEncrypt;
std::uint8_t data[SIZE];
};
// mode parameter for constructor
enum {
ENCRYPT = 1,
DECRYPT = 0
};
TokenEncrypt(const Key& key, const int mode)
{
ctx = EVP_CIPHER_CTX_new ();
EVP_CIPHER_CTX_reset (ctx);
if (!EVP_CipherInit_ex(ctx, EVP_aes_128_ecb(), nullptr, key.data, nullptr, mode))
{
EVP_CIPHER_CTX_free(ctx);
throw OpenSSLException("TokenEncrypt: EVP_CipherInit_ex[1] failed");
}
EVP_CIPHER_CTX_set_padding(ctx, 0);
}
~TokenEncrypt()
{
EVP_CIPHER_CTX_free(ctx);
}
// Do the encrypt/decrypt
void operator()(std::uint8_t* dest, const std::uint8_t* src, const size_t size)
{
// NOTE: since this algorithm uses the ECB block cipher mode,
// it should only be used to encrypt/decrypt a message which
// is exactly equal to the AES block size (16 bytes).
if (size != EVP_CIPHER_CTX_block_size(ctx))
throw Exception("TokenEncrypt: encrypt/decrypt data must be equal to AES block size");
int outlen=0;
if (!EVP_CipherInit_ex(ctx, nullptr, nullptr, nullptr, nullptr, -1))
throw OpenSSLException("TokenEncrypt: EVP_CipherInit_ex[2] failed");
if (!EVP_CipherUpdate(ctx, dest, &outlen, src, size))
throw OpenSSLException("TokenEncrypt: EVP_CipherUpdate failed");
// NOTE: we skip EVP_CipherFinal_ex because we are running in ECB mode without padding
if (outlen != size)
throw Exception("TokenEncrypt: unexpected output length=" + std::to_string(outlen) + " expected=" + std::to_string(size));
}
private:
TokenEncrypt(const TokenEncrypt&) = delete;
TokenEncrypt& operator=(const TokenEncrypt&) = delete;
EVP_CIPHER_CTX* ctx;
};
struct TokenEncryptDecrypt
{
TokenEncryptDecrypt(const TokenEncrypt::Key& key)
: encrypt(key, TokenEncrypt::ENCRYPT),
decrypt(key, TokenEncrypt::DECRYPT)
{
}
TokenEncrypt encrypt;
TokenEncrypt decrypt;
};
}
#endif