mirror of
https://github.com/mangosone/server
synced 2026-08-18 22:26:15 -04:00
RC4 comes into the tree, and the legacy provider goes out of the deployment
Thirty lines of cipher were costing a DLL. OpenSSL 3 moved RC4 to the legacy provider, which is not compiled into libcrypto -- it is a separate module discovered on disk at run time. So a 256-byte permutation dragged in ossl-modules/legacy.dll beside the executable, an OPENSSL_MODULES search path, CMake that hunted for the file and copied it, CI that pointed an environment variable at it, an installer obliged to ship it, and a start-up check in both daemons that REFUSED TO BOOT without it. It was also a loaded gun. The legacy provider is deprecated; the release that finally drops it would have stopped this emulator starting, for want of a cipher any competent programmer can write from the specification. And the protocol needs RC4 forever, because the 2.4.3 client is never going to be updated -- a cipher the wire mandates in perpetuity belongs in the tree, not behind a compatibility shim someone else maintains for our convenience. So: ARCFOUR, written out. A key schedule, a permutation, a stream that XORs. The class keeps its interface, so no caller changed -- AuthCrypt and Warden were not touched. CryptoStressTest was not touched either, and that is the point: `Crypto_arc4_matches_the_published_vector` passes against the same published vector it always checked, so the equivalence is demonstrated rather than claimed. Two details worth keeping. Init rebuilds the permutation from the identity every time, because it is a RE-key as often as a first key and keying on top of a used state would make the stream depend on how much traffic preceded it; and it rewinds the stream position, forgetting which is the classic way to build a cipher that decrypts the first session and nothing after it. OpenSSLProvider and its test are deleted outright. RC4 was the only thing this tree ever asked the legacy provider for; SHA-1, SHA-256, AES and the bignum work all live in the DEFAULT provider, which is inside libcrypto and needs nothing on the side. The mangosd binary now contains no reference to ossl-modules, to OSSL_PROVIDER, or to a legacy provider at all. Linking is unchanged and still dynamic. This removes a module that had to be FOUND at run time, which is a different problem from where libcrypto itself comes from. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
3117b99f6b
commit
5c87487ca1
9 changed files with 146 additions and 934 deletions
3
.github/workflows/core_windows_build.yml
vendored
3
.github/workflows/core_windows_build.yml
vendored
|
|
@ -87,9 +87,6 @@ jobs:
|
|||
echo "OPENSSL_INCLUDE_DIR=$root/include" >> "$GITHUB_ENV"
|
||||
echo "OPENSSL_CRYPTO_LIBRARY=$root/lib/VC/libcrypto64MT.lib" >> "$GITHUB_ENV"
|
||||
echo "OPENSSL_SSL_LIBRARY=$root/lib/VC/libssl64MT.lib" >> "$GITHUB_ENV"
|
||||
# SlProWeb installs provider DLLs beside openssl.exe, while its
|
||||
# compiled MODULESDIR still names the default installation path.
|
||||
echo "OPENSSL_MODULES=$root/bin" >> "$GITHUB_ENV"
|
||||
echo "$root/bin" >> "$GITHUB_PATH"
|
||||
else
|
||||
echo "::error::OpenSSL developer libraries not found"
|
||||
|
|
|
|||
|
|
@ -47,10 +47,6 @@
|
|||
#include "Common/ServerDefines.h"
|
||||
#include <openssl/opensslv.h>
|
||||
#include <openssl/crypto.h>
|
||||
#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
|
||||
# include <openssl/provider.h>
|
||||
# include "Auth/OpenSSLProvider.h"
|
||||
#endif
|
||||
|
||||
#include "Platform/Define.h"
|
||||
#include <cstdio>
|
||||
|
|
@ -305,13 +301,7 @@ int main(int argc, char** argv)
|
|||
|
||||
DETAIL_LOG("Using SSL version: %s (Library: %s)", OPENSSL_VERSION_TEXT, OpenSSL_version(OPENSSL_VERSION));
|
||||
|
||||
#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
|
||||
if (!OpenSSLProviderManager::Instance().IsInitialized())
|
||||
{
|
||||
Log::WaitBeforeContinueIfNeed();
|
||||
return 1;
|
||||
}
|
||||
#else
|
||||
#if !defined(OPENSSL_VERSION_MAJOR) || (OPENSSL_VERSION_MAJOR < 3)
|
||||
if (SSLeay() < 0x10100000L || SSLeay() > 0x10200000L)
|
||||
{
|
||||
DETAIL_LOG("WARNING: OpenSSL version may be out of date or unsupported. Logins to server may not work!");
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2026 MaNGOS <https://www.getmangos.eu>
|
||||
* Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -24,163 +23,117 @@
|
|||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file ARC4.cpp
|
||||
* @brief Implementation of ARC4 encryption algorithm using OpenSSL
|
||||
*
|
||||
* This file implements the ARC4 (Alleged RC4) stream cipher for use
|
||||
* in the MaNGOS authentication and session encryption system. ARC4
|
||||
* is used to encrypt/decrypt game traffic between the server and clients.
|
||||
*
|
||||
* The implementation uses OpenSSL's EVP interface for the cipher operations
|
||||
* and includes proper provider management for OpenSSL 3.x compatibility.
|
||||
*/
|
||||
|
||||
#include "ARC4.h"
|
||||
#include "OpenSSLProvider.h"
|
||||
#include "Log/Log.h"
|
||||
#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
|
||||
#include <openssl/provider.h>
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Construct ARC4 cipher with specified key length
|
||||
* @param len Key length in bytes
|
||||
*
|
||||
* Creates an ARC4 cipher context with the specified key length.
|
||||
* The key itself is not set in this constructor - use Init() to set it.
|
||||
*
|
||||
* @note On OpenSSL 3.x, this automatically initializes the legacy provider
|
||||
* required for ARC4 support.
|
||||
*/
|
||||
ARC4::ARC4(uint8 len) : m_cipherContext()
|
||||
{
|
||||
#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
|
||||
// RC4 lives in the legacy provider, so it has to be loaded before EVP_rc4().
|
||||
if (!OpenSSLProviderManager::Instance().IsInitialized())
|
||||
{
|
||||
sLog.outError("ARC4: Failed to initialize OpenSSL providers");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#include <cstring>
|
||||
|
||||
if (!m_cipherContext.IsValid())
|
||||
namespace
|
||||
{
|
||||
sLog.outError("ARC4: Failed to create cipher context");
|
||||
return;
|
||||
/// The permutation is a byte table, so every index is taken modulo 256 -- which is
|
||||
/// what a uint8 does on its own. Saying so once beats masking at eight call sites.
|
||||
inline void Swap(uint8& a, uint8& b)
|
||||
{
|
||||
const uint8 t = a;
|
||||
a = b;
|
||||
b = t;
|
||||
}
|
||||
|
||||
// Checked, not assumed: a loaded provider does not mean the cipher was fetched.
|
||||
if (EVP_EncryptInit_ex(m_cipherContext.Get(), EVP_rc4(), NULL, NULL, NULL) != 1)
|
||||
/// A key of zero bytes would divide by zero in the schedule below, and a key longer
|
||||
/// than the permutation cannot be distinguished from its first 256 bytes.
|
||||
inline uint8 UsableKeyLength(uint8 len)
|
||||
{
|
||||
sLog.outError("ARC4: Failed to initialize RC4 - is the OpenSSL legacy provider loaded?");
|
||||
return;
|
||||
}
|
||||
|
||||
if (EVP_CIPHER_CTX_set_key_length(m_cipherContext.Get(), len) != 1)
|
||||
{
|
||||
sLog.outError("ARC4: Failed to set the RC4 key length");
|
||||
return;
|
||||
return len ? len : 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Construct ARC4 cipher with initial key
|
||||
* @param seed Pointer to the key bytes
|
||||
* @param len Length of the key in bytes
|
||||
*
|
||||
* Creates an ARC4 cipher context and initializes it with the provided key.
|
||||
* The cipher is ready to use for encryption/decryption immediately after
|
||||
* construction.
|
||||
*
|
||||
* @note On OpenSSL 3.x, this automatically initializes the legacy provider
|
||||
* required for ARC4 support.
|
||||
*/
|
||||
ARC4::ARC4(uint8 *seed, uint8 len) : m_cipherContext()
|
||||
ARC4::ARC4(uint8 len)
|
||||
: m_x(0), m_y(0), m_keyLength(UsableKeyLength(len))
|
||||
{
|
||||
#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
|
||||
// RC4 lives in the legacy provider, so it has to be loaded before EVP_rc4().
|
||||
if (!OpenSSLProviderManager::Instance().IsInitialized())
|
||||
// The identity permutation. Not keyed yet: a caller using this constructor has said
|
||||
// it will supply the key through Init, and leaving the state identity means a
|
||||
// forgotten Init produces an obviously wrong stream rather than a plausible one.
|
||||
for (int i = 0; i < 256; ++i)
|
||||
{
|
||||
sLog.outError("ARC4: Failed to initialize OpenSSL providers");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!m_cipherContext.IsValid())
|
||||
{
|
||||
sLog.outError("ARC4: Failed to create cipher context");
|
||||
return;
|
||||
}
|
||||
|
||||
// Checked, not assumed: a loaded provider does not mean the cipher was fetched.
|
||||
if (EVP_EncryptInit_ex(m_cipherContext.Get(), EVP_rc4(), NULL, NULL, NULL) != 1)
|
||||
{
|
||||
sLog.outError("ARC4: Failed to initialize RC4 - is the OpenSSL legacy provider loaded?");
|
||||
return;
|
||||
}
|
||||
|
||||
if (EVP_CIPHER_CTX_set_key_length(m_cipherContext.Get(), len) != 1)
|
||||
{
|
||||
sLog.outError("ARC4: Failed to set the RC4 key length");
|
||||
return;
|
||||
}
|
||||
|
||||
if (EVP_EncryptInit_ex(m_cipherContext.Get(), NULL, NULL, seed, NULL) != 1)
|
||||
{
|
||||
sLog.outError("ARC4: Failed to seed RC4");
|
||||
m_state[i] = uint8(i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destructor for ARC4 cipher
|
||||
*
|
||||
* All cleanup is handled automatically by the RAII wrappers
|
||||
* (OpenSSLProviderManager and OpenSSLCipherContext).
|
||||
*/
|
||||
ARC4::ARC4(uint8* seed, uint8 len)
|
||||
: m_x(0), m_y(0), m_keyLength(UsableKeyLength(len))
|
||||
{
|
||||
for (int i = 0; i < 256; ++i)
|
||||
{
|
||||
m_state[i] = uint8(i);
|
||||
}
|
||||
Init(seed);
|
||||
}
|
||||
|
||||
ARC4::~ARC4()
|
||||
{
|
||||
// Cleanup is now handled automatically by RAII wrappers
|
||||
// The key schedule is derived from a session secret, so it does not outlive the
|
||||
// object in freed memory waiting to be read back. memset rather than std::fill
|
||||
// because the intent is erasure, and a loop over a member the compiler can see is
|
||||
// dead is a loop the compiler may delete.
|
||||
std::memset(m_state, 0, sizeof(m_state));
|
||||
m_x = 0;
|
||||
m_y = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize or re-initialize the cipher with a new key
|
||||
* @param seed Pointer to the key bytes
|
||||
*
|
||||
* Sets or changes the encryption key for the ARC4 cipher.
|
||||
* This can be called multiple times to re-key the cipher.
|
||||
*
|
||||
* @note The key length must match the length specified in the constructor.
|
||||
*/
|
||||
void ARC4::Init(uint8* seed)
|
||||
{
|
||||
if (m_cipherContext.IsValid())
|
||||
if (!seed)
|
||||
{
|
||||
EVP_EncryptInit_ex(m_cipherContext.Get(), NULL, NULL, seed, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Encrypt or decrypt data in-place
|
||||
* @param len Length of data to process in bytes
|
||||
* @param data Pointer to the data buffer (modified in-place)
|
||||
*
|
||||
* Processes data using the ARC4 stream cipher. Since ARC4 is a symmetric
|
||||
* stream cipher, the same operation is used for both encryption and
|
||||
* decryption. The data is modified in-place for efficiency.
|
||||
*
|
||||
* @warning The cipher must be initialized with a key before calling this.
|
||||
* @note The output length will always equal the input length for ARC4.
|
||||
*/
|
||||
void ARC4::UpdateData(int len, uint8 *data)
|
||||
{
|
||||
if (!m_cipherContext.IsValid())
|
||||
{
|
||||
sLog.outError("ARC4: Invalid cipher context, cannot update data");
|
||||
return;
|
||||
}
|
||||
|
||||
int outlen = 0;
|
||||
EVP_EncryptUpdate(m_cipherContext.Get(), data, &outlen, data, len);
|
||||
EVP_EncryptFinal_ex(m_cipherContext.Get(), data, &outlen);
|
||||
// === Key schedule (KSA) ===
|
||||
//
|
||||
// Start from the identity every time. Init is a RE-key as much as a first key --
|
||||
// the packet crypt builds a cipher and seeds it afterwards -- and keying on top of
|
||||
// a used permutation would produce a stream that depends on how much traffic had
|
||||
// gone before it.
|
||||
for (int i = 0; i < 256; ++i)
|
||||
{
|
||||
m_state[i] = uint8(i);
|
||||
}
|
||||
|
||||
uint8 j = 0;
|
||||
for (int i = 0; i < 256; ++i)
|
||||
{
|
||||
j = uint8(j + m_state[i] + seed[i % m_keyLength]);
|
||||
Swap(m_state[i], m_state[j]);
|
||||
}
|
||||
|
||||
// A stream cipher is its position in the stream, so a re-key rewinds it. Forgetting
|
||||
// this is the classic way to make a cipher that decrypts the first session and
|
||||
// nothing after it.
|
||||
m_x = 0;
|
||||
m_y = 0;
|
||||
}
|
||||
|
||||
void ARC4::UpdateData(int len, uint8* data)
|
||||
{
|
||||
if (len <= 0 || !data)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// === Pseudo-random generation (PRGA) ===
|
||||
//
|
||||
// Kept in locals and written back once: the two indices are read and written for
|
||||
// every byte, and leaving them as members makes the compiler reload them through
|
||||
// `this` each time in case `data` aliases the object.
|
||||
uint8 x = m_x;
|
||||
uint8 y = m_y;
|
||||
|
||||
for (int n = 0; n < len; ++n)
|
||||
{
|
||||
x = uint8(x + 1);
|
||||
y = uint8(y + m_state[x]);
|
||||
Swap(m_state[x], m_state[y]);
|
||||
data[n] ^= m_state[uint8(m_state[x] + m_state[y])];
|
||||
}
|
||||
|
||||
m_x = x;
|
||||
m_y = y;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2026 MaNGOS <https://www.getmangos.eu>
|
||||
* Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
|
@ -27,47 +26,77 @@
|
|||
#ifndef _AUTH_SARC4_H
|
||||
#define _AUTH_SARC4_H
|
||||
|
||||
#include <openssl/evp.h>
|
||||
#include "Platform/Define.h"
|
||||
#include "OpenSSLProvider.h"
|
||||
|
||||
/**
|
||||
* @brief ARC4 encryption/decryption cipher implementation
|
||||
* @brief ARCFOUR, in about thirty lines, because the alternative was a DLL.
|
||||
*
|
||||
* ARC4 is a stream cipher that uses a key to generate a keystream.
|
||||
* This class provides ARC4 encryption and decryption functionality using OpenSSL.
|
||||
* This used to be EVP_rc4(). OpenSSL 3 moved RC4 to the LEGACY provider, which is not
|
||||
* compiled into libcrypto -- it is a separate module discovered on disk at run time. So
|
||||
* a cipher of two hundred and fifty-six bytes of state dragged in a deployment
|
||||
* requirement (`ossl-modules/legacy.dll` beside the executable, an OPENSSL_MODULES
|
||||
* search path, an installer that ships it, CI that copies it) and a start-up check that
|
||||
* REFUSED TO RUN without it.
|
||||
*
|
||||
* It also aimed a loaded gun at the server: the legacy provider is deprecated, and the
|
||||
* release that finally drops it would have stopped this emulator booting -- for want of
|
||||
* a cipher any competent programmer can write from the specification.
|
||||
*
|
||||
* The protocol needs RC4 and will always need it, because the 2.4.3 client is never
|
||||
* going to be updated. A cipher the protocol mandates forever belongs in the tree.
|
||||
*
|
||||
* ARCFOUR is fully specified and this is the whole of it: a 256-byte permutation, a
|
||||
* key-scheduling pass, and a stream that XORs. There is no interoperability risk of the
|
||||
* kind that would justify borrowing an implementation -- CryptoStressTest exercises the
|
||||
* same vectors it always did, and it was not touched, so the equivalence is demonstrated
|
||||
* rather than asserted.
|
||||
*
|
||||
* NOT thread-safe, and it must not be: a stream cipher IS its position in the stream, so
|
||||
* two threads sharing one is a protocol error, not a race to be locked away. Each
|
||||
* direction of each session owns its own.
|
||||
*/
|
||||
class ARC4
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor with key length specification
|
||||
* @param len Length of the key in bytes
|
||||
* @brief A cipher expecting a key of `len` bytes, keyed later by Init().
|
||||
* @param len Key length in bytes, 1..256.
|
||||
*/
|
||||
ARC4(uint8 len);
|
||||
explicit ARC4(uint8 len);
|
||||
|
||||
/**
|
||||
* @brief Constructor with seed data
|
||||
* @param seed Pointer to the seed/key data
|
||||
* @param len Length of the seed data in bytes
|
||||
* @brief A cipher keyed immediately.
|
||||
* @param seed Key bytes.
|
||||
* @param len Key length in bytes, 1..256.
|
||||
*/
|
||||
ARC4(uint8* seed, uint8 len);
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
|
||||
~ARC4();
|
||||
|
||||
/**
|
||||
* @brief Initialize the cipher with seed data
|
||||
* @param seed Pointer to the seed/key data
|
||||
* @brief (Re)key the cipher and rewind the keystream.
|
||||
*
|
||||
* The length is the one given to the constructor -- the signature has no room
|
||||
* for another, and every caller in the tree keys with the length it declared.
|
||||
*
|
||||
* @param seed Key bytes; at least the constructor's length must be readable.
|
||||
*/
|
||||
void Init(uint8* seed);
|
||||
|
||||
/**
|
||||
* @brief Update/encrypt data using the cipher
|
||||
* @param len Length of the data to process
|
||||
* @param data Pointer to the data to encrypt/decrypt
|
||||
* @brief XOR `len` bytes of `data` with the keystream, in place.
|
||||
*
|
||||
* Encryption and decryption are the same operation, which is why one method
|
||||
* serves both and why calling it twice on the same bytes with the same key
|
||||
* returns the original.
|
||||
*/
|
||||
void UpdateData(int len, uint8* data);
|
||||
|
||||
private:
|
||||
OpenSSLCipherContext m_cipherContext; /**< RAII cipher context */
|
||||
uint8 m_state[256]; ///< The permutation.
|
||||
uint8 m_x; ///< Stream position; `i` in the specification.
|
||||
uint8 m_y; ///< Stream position; `j` in the specification.
|
||||
uint8 m_keyLength; ///< Bytes Init() will read from its seed.
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,453 +0,0 @@
|
|||
/**
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2026 MaNGOS <https://www.getmangos.eu>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file OpenSSLProvider.cpp
|
||||
* @brief Implementation of RAII wrappers for OpenSSL providers
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include "OpenSSLProvider.h"
|
||||
#include "Log/Log.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <charconv>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <openssl/core_names.h>
|
||||
#include <openssl/params.h>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Creates a new OpenSSL cipher context wrapper.
|
||||
*/
|
||||
OpenSSLCipherContext::OpenSSLCipherContext()
|
||||
: m_ctx(nullptr)
|
||||
{
|
||||
m_ctx = EVP_CIPHER_CTX_new();
|
||||
if (!m_ctx)
|
||||
{
|
||||
sLog.outError("OpenSSLCipherContext: Failed to create cipher context");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases the owned OpenSSL cipher context.
|
||||
*/
|
||||
OpenSSLCipherContext::~OpenSSLCipherContext()
|
||||
{
|
||||
if (m_ctx)
|
||||
{
|
||||
EVP_CIPHER_CTX_free(m_ctx);
|
||||
m_ctx = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
OpenSSLCipherContext::OpenSSLCipherContext(OpenSSLCipherContext&& other) noexcept
|
||||
: m_ctx(other.m_ctx)
|
||||
{
|
||||
other.m_ctx = nullptr;
|
||||
}
|
||||
|
||||
OpenSSLCipherContext& OpenSSLCipherContext::operator=(OpenSSLCipherContext&& other) noexcept
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
// Clean up current context
|
||||
if (m_ctx)
|
||||
{
|
||||
EVP_CIPHER_CTX_free(m_ctx);
|
||||
}
|
||||
|
||||
// Move from other
|
||||
m_ctx = other.m_ctx;
|
||||
other.m_ctx = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
|
||||
|
||||
namespace
|
||||
{
|
||||
bool ParseProviderMajor(const std::string& version, unsigned& major)
|
||||
{
|
||||
std::size_t separator = version.find('.');
|
||||
if (separator == std::string::npos || separator == 0)
|
||||
return false;
|
||||
|
||||
const char* begin = version.data();
|
||||
const char* end = begin + separator;
|
||||
std::from_chars_result parsed =
|
||||
std::from_chars(begin, end, major);
|
||||
return parsed.ec == std::errc{} && parsed.ptr == end;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
std::wstring ReadWindowsEnvironment(const wchar_t* name)
|
||||
{
|
||||
DWORD required = GetEnvironmentVariableW(name, nullptr, 0);
|
||||
if (required == 0)
|
||||
return {};
|
||||
|
||||
std::vector<wchar_t> buffer(required);
|
||||
while (true)
|
||||
{
|
||||
DWORD written = GetEnvironmentVariableW(
|
||||
name, buffer.data(), static_cast<DWORD>(buffer.size()));
|
||||
if (written == 0)
|
||||
return {};
|
||||
if (written < buffer.size())
|
||||
return std::wstring(buffer.data(), written);
|
||||
buffer.resize(static_cast<std::size_t>(written) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
std::filesystem::path GetExecutableDirectory()
|
||||
{
|
||||
constexpr std::size_t MAX_WINDOWS_PATH = 32768;
|
||||
std::vector<wchar_t> buffer(MAX_PATH);
|
||||
|
||||
while (buffer.size() <= MAX_WINDOWS_PATH)
|
||||
{
|
||||
DWORD written = GetModuleFileNameW(
|
||||
nullptr, buffer.data(), static_cast<DWORD>(buffer.size()));
|
||||
if (written == 0)
|
||||
return {};
|
||||
if (written < buffer.size())
|
||||
{
|
||||
return std::filesystem::path(
|
||||
std::wstring(buffer.data(), written)).parent_path();
|
||||
}
|
||||
|
||||
if (buffer.size() == MAX_WINDOWS_PATH)
|
||||
break;
|
||||
buffer.resize((std::min)(buffer.size() * 2, MAX_WINDOWS_PATH));
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
bool ConvertWideToAnsi(const std::wstring& value, std::string& converted)
|
||||
{
|
||||
converted.clear();
|
||||
if (value.empty())
|
||||
return false;
|
||||
|
||||
constexpr UINT GB18030_CODE_PAGE = 54936;
|
||||
UINT const codePage = GetACP();
|
||||
bool const restrictedCodePage =
|
||||
codePage == CP_UTF8 || codePage == GB18030_CODE_PAGE;
|
||||
DWORD const flags = restrictedCodePage ?
|
||||
WC_ERR_INVALID_CHARS : WC_NO_BEST_FIT_CHARS;
|
||||
BOOL usedDefault = FALSE;
|
||||
BOOL* usedDefaultPointer = restrictedCodePage ? nullptr : &usedDefault;
|
||||
int required = WideCharToMultiByte(
|
||||
codePage, flags, value.c_str(), -1,
|
||||
nullptr, 0, nullptr, usedDefaultPointer);
|
||||
if (required <= 0 || usedDefault)
|
||||
return false;
|
||||
|
||||
std::vector<char> buffer(static_cast<std::size_t>(required));
|
||||
usedDefault = FALSE;
|
||||
int written = WideCharToMultiByte(
|
||||
codePage, flags, value.c_str(), -1,
|
||||
buffer.data(), required, nullptr, usedDefaultPointer);
|
||||
if (written <= 0 || usedDefault)
|
||||
return false;
|
||||
|
||||
converted.assign(buffer.data(), static_cast<std::size_t>(written - 1));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsUsableOpenSSLPath(const std::wstring& path, std::string& converted)
|
||||
{
|
||||
if (!ConvertWideToAnsi(path, converted))
|
||||
return false;
|
||||
|
||||
constexpr std::size_t LEGACY_SUFFIX_LENGTH = sizeof("\\legacy.dll") - 1;
|
||||
return converted.size() + LEGACY_SUFFIX_LENGTH < MAX_PATH;
|
||||
}
|
||||
|
||||
bool ConvertForOpenSSL(
|
||||
const std::filesystem::path& directory, std::string& converted)
|
||||
{
|
||||
const std::wstring& nativePath = directory.native();
|
||||
if (IsUsableOpenSSLPath(nativePath, converted))
|
||||
return true;
|
||||
|
||||
DWORD required = GetShortPathNameW(nativePath.c_str(), nullptr, 0);
|
||||
if (required == 0)
|
||||
return false;
|
||||
|
||||
std::vector<wchar_t> shortPath(required);
|
||||
DWORD written = GetShortPathNameW(
|
||||
nativePath.c_str(), shortPath.data(), required);
|
||||
if (written == 0 || written >= shortPath.size())
|
||||
return false;
|
||||
|
||||
return IsUsableOpenSSLPath(
|
||||
std::wstring(shortPath.data(), written), converted);
|
||||
}
|
||||
|
||||
void ConfigureBundledProviderSearchPath()
|
||||
{
|
||||
if (!ReadWindowsEnvironment(L"OPENSSL_MODULES").empty())
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
std::filesystem::path executableDirectory = GetExecutableDirectory();
|
||||
if (executableDirectory.empty())
|
||||
{
|
||||
sLog.outError(
|
||||
"OpenSSLProvider: Failed to resolve the executable directory");
|
||||
return;
|
||||
}
|
||||
|
||||
std::filesystem::path providerDirectory =
|
||||
executableDirectory / L"ossl-modules";
|
||||
std::filesystem::path legacyProvider =
|
||||
providerDirectory / L"legacy.dll";
|
||||
std::error_code fileError;
|
||||
if (!std::filesystem::is_regular_file(legacyProvider, fileError))
|
||||
return;
|
||||
|
||||
std::string providerPath;
|
||||
if (!ConvertForOpenSSL(providerDirectory, providerPath))
|
||||
{
|
||||
sLog.outError(
|
||||
"OpenSSLProvider: Bundled provider path cannot be represented "
|
||||
"for the OpenSSL Windows loader");
|
||||
return;
|
||||
}
|
||||
|
||||
if (OSSL_PROVIDER_set_default_search_path(
|
||||
nullptr, providerPath.c_str()) != 1)
|
||||
{
|
||||
sLog.outError(
|
||||
"OpenSSLProvider: Failed to configure bundled provider path '%s'",
|
||||
providerPath.c_str());
|
||||
}
|
||||
}
|
||||
catch (const std::filesystem::filesystem_error& error)
|
||||
{
|
||||
sLog.outError(
|
||||
"OpenSSLProvider: Failed to inspect bundled provider path: %s",
|
||||
error.what());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
OpenSSLProvider LoadLegacyProvider()
|
||||
{
|
||||
#ifdef WIN32
|
||||
ConfigureBundledProviderSearchPath();
|
||||
#endif
|
||||
return OpenSSLProvider("legacy");
|
||||
}
|
||||
|
||||
std::string OpenSSLModulesForDiagnostic()
|
||||
{
|
||||
#ifdef WIN32
|
||||
std::wstring modules = ReadWindowsEnvironment(L"OPENSSL_MODULES");
|
||||
if (modules.empty())
|
||||
return "<unset>";
|
||||
|
||||
std::string converted;
|
||||
return ConvertWideToAnsi(modules, converted)
|
||||
? converted : "<unrepresentable>";
|
||||
#else
|
||||
const char* modules = std::getenv("OPENSSL_MODULES");
|
||||
return modules ? std::string(modules) : std::string("<unset>");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the named OpenSSL provider into the specified library context.
|
||||
*/
|
||||
OpenSSLProvider::OpenSSLProvider(const char* name, OSSL_LIB_CTX* libraryContext)
|
||||
: m_provider(nullptr), m_providerName(name ? name : "")
|
||||
{
|
||||
if (!name)
|
||||
{
|
||||
sLog.outError("OpenSSLProvider: Provider name cannot be null");
|
||||
return;
|
||||
}
|
||||
|
||||
m_provider = OSSL_PROVIDER_load(libraryContext, name);
|
||||
if (!m_provider)
|
||||
{
|
||||
sLog.outError("OpenSSLProvider: Failed to load provider '%s'", name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unloads the owned OpenSSL provider instance.
|
||||
*/
|
||||
OpenSSLProvider::~OpenSSLProvider()
|
||||
{
|
||||
if (m_provider)
|
||||
{
|
||||
OSSL_PROVIDER_unload(m_provider);
|
||||
m_provider = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::string OpenSSLProvider::Version() const
|
||||
{
|
||||
if (!m_provider)
|
||||
return {};
|
||||
|
||||
char* version = nullptr;
|
||||
OSSL_PARAM params[] = {
|
||||
OSSL_PARAM_construct_utf8_ptr(
|
||||
OSSL_PROV_PARAM_VERSION, &version, 0),
|
||||
OSSL_PARAM_construct_end()
|
||||
};
|
||||
if (OSSL_PROVIDER_get_params(m_provider, params) != 1 || !version)
|
||||
return {};
|
||||
return version;
|
||||
}
|
||||
|
||||
OpenSSLProvider::OpenSSLProvider(OpenSSLProvider&& other) noexcept
|
||||
: m_provider(other.m_provider), m_providerName(std::move(other.m_providerName))
|
||||
{
|
||||
other.m_provider = nullptr;
|
||||
}
|
||||
|
||||
OpenSSLProvider& OpenSSLProvider::operator=(OpenSSLProvider&& other) noexcept
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
// Clean up current provider
|
||||
if (m_provider)
|
||||
{
|
||||
OSSL_PROVIDER_unload(m_provider);
|
||||
}
|
||||
|
||||
// Move from other
|
||||
m_provider = other.m_provider;
|
||||
m_providerName = std::move(other.m_providerName);
|
||||
other.m_provider = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the OpenSSL provider manager and loads required providers.
|
||||
*/
|
||||
OpenSSLProviderManager::OpenSSLProviderManager()
|
||||
: m_legacyProvider(LoadLegacyProvider()),
|
||||
m_defaultProvider("default"),
|
||||
m_initialized(false)
|
||||
{
|
||||
if (!m_legacyProvider.IsLoaded() || !m_defaultProvider.IsLoaded())
|
||||
{
|
||||
sLog.outError("Failed to load OpenSSL 3.x providers");
|
||||
|
||||
if (!m_legacyProvider.IsLoaded())
|
||||
{
|
||||
sLog.outError(" - Legacy provider failed to load");
|
||||
#ifdef WIN32
|
||||
sLog.outError(" Use a complete release with ossl-modules\\legacy.dll");
|
||||
sLog.outError(" beside the daemon, or set OPENSSL_MODULES to the");
|
||||
sLog.outError(" directory containing a matching legacy.dll.");
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!m_defaultProvider.IsLoaded())
|
||||
{
|
||||
sLog.outError(" - Default provider failed to load");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
std::string legacyVersion = m_legacyProvider.Version();
|
||||
std::string defaultVersion = m_defaultProvider.Version();
|
||||
unsigned legacyMajor = 0;
|
||||
unsigned defaultMajor = 0;
|
||||
bool parsedLegacy =
|
||||
ParseProviderMajor(legacyVersion, legacyMajor);
|
||||
bool parsedDefault =
|
||||
ParseProviderMajor(defaultVersion, defaultMajor);
|
||||
|
||||
unsigned runtimeMajor =
|
||||
unsigned((OpenSSL_version_num() >> 28) & 0x0f);
|
||||
if (runtimeMajor != 3
|
||||
|| !parsedLegacy || legacyMajor != runtimeMajor
|
||||
|| !parsedDefault || defaultMajor != runtimeMajor)
|
||||
{
|
||||
std::string modules = OpenSSLModulesForDiagnostic();
|
||||
sLog.outError(
|
||||
"OpenSSL 3.x provider/runtime validation failed: "
|
||||
"runtime='%s', legacy provider='%s', default provider='%s', "
|
||||
"OPENSSL_MODULES='%s'",
|
||||
OpenSSL_version(OPENSSL_VERSION),
|
||||
legacyVersion.empty() ? "<unavailable>" : legacyVersion.c_str(),
|
||||
defaultVersion.empty() ? "<unavailable>" : defaultVersion.c_str(),
|
||||
modules.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
EVP_CIPHER* rc4 = EVP_CIPHER_fetch(nullptr, "RC4", nullptr);
|
||||
if (!rc4)
|
||||
{
|
||||
sLog.outError(
|
||||
"OpenSSL legacy provider is loaded but RC4 is unavailable");
|
||||
return;
|
||||
}
|
||||
EVP_CIPHER_free(rc4);
|
||||
|
||||
m_initialized = true;
|
||||
sLog.outString(
|
||||
"OpenSSL 3.x providers loaded successfully: legacy %s, default %s",
|
||||
legacyVersion.c_str(), defaultVersion.c_str());
|
||||
}
|
||||
|
||||
OpenSSLProviderManager& OpenSSLProviderManager::Instance()
|
||||
{
|
||||
static OpenSSLProviderManager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs provider shutdown when the OpenSSL provider manager is destroyed.
|
||||
*/
|
||||
OpenSSLProviderManager::~OpenSSLProviderManager()
|
||||
{
|
||||
if (m_initialized)
|
||||
{
|
||||
sLog.outString("OpenSSL 3.x providers unloaded");
|
||||
}
|
||||
}
|
||||
|
||||
#endif // OPENSSL_VERSION_MAJOR >= 3
|
||||
|
|
@ -1,206 +0,0 @@
|
|||
/**
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2026 MaNGOS <https://www.getmangos.eu>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file OpenSSLProvider.h
|
||||
* @brief RAII wrapper for OpenSSL 3.x providers
|
||||
*
|
||||
* This file provides RAII wrappers for OpenSSL provider management,
|
||||
* ensuring proper cleanup and exception safety for OpenSSL 3.x
|
||||
* provider loading and unloading.
|
||||
*/
|
||||
|
||||
#ifndef _AUTH_OPENSSL_PROVIDER_H
|
||||
#define _AUTH_OPENSSL_PROVIDER_H
|
||||
|
||||
#include <string>
|
||||
#include <openssl/evp.h>
|
||||
|
||||
/**
|
||||
* @brief RAII wrapper for EVP_CIPHER_CTX
|
||||
*
|
||||
* This class manages EVP_CIPHER_CTX lifecycle with proper
|
||||
* initialization and cleanup, preventing memory leaks.
|
||||
*/
|
||||
class OpenSSLCipherContext
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor - creates new cipher context
|
||||
*/
|
||||
OpenSSLCipherContext();
|
||||
|
||||
/**
|
||||
* @brief Destructor - automatically frees context
|
||||
*/
|
||||
~OpenSSLCipherContext();
|
||||
|
||||
/**
|
||||
* @brief Get the underlying cipher context
|
||||
* @return EVP_CIPHER_CTX pointer
|
||||
*/
|
||||
EVP_CIPHER_CTX* Get() const { return m_ctx; }
|
||||
|
||||
/**
|
||||
* @brief Check if context is valid
|
||||
* @return true if context is valid, false otherwise
|
||||
*/
|
||||
bool IsValid() const { return m_ctx != nullptr; }
|
||||
|
||||
/**
|
||||
* @brief Move constructor
|
||||
*/
|
||||
OpenSSLCipherContext(OpenSSLCipherContext&& other) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Move assignment operator
|
||||
*/
|
||||
OpenSSLCipherContext& operator=(OpenSSLCipherContext&& other) noexcept;
|
||||
|
||||
// Delete copy operations
|
||||
OpenSSLCipherContext(const OpenSSLCipherContext&) = delete;
|
||||
OpenSSLCipherContext& operator=(const OpenSSLCipherContext&) = delete;
|
||||
|
||||
private:
|
||||
EVP_CIPHER_CTX* m_ctx; /**< OpenSSL cipher context */
|
||||
};
|
||||
|
||||
#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
|
||||
#include <openssl/provider.h>
|
||||
|
||||
/**
|
||||
* @brief RAII wrapper for OpenSSL 3.x OSSL_PROVIDER
|
||||
*
|
||||
* This class automatically loads and unloads OpenSSL providers,
|
||||
* ensuring proper cleanup when the wrapper goes out of scope.
|
||||
* It provides exception safety and prevents resource leaks.
|
||||
*/
|
||||
class OpenSSLProvider
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor - loads specified provider
|
||||
* @param name Provider name (e.g., "legacy", "default")
|
||||
* @param libraryContext Library context (NULL for default)
|
||||
*/
|
||||
OpenSSLProvider(const char* name, OSSL_LIB_CTX* libraryContext = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Destructor - automatically unloads provider
|
||||
*/
|
||||
~OpenSSLProvider();
|
||||
|
||||
/**
|
||||
* @brief Check if provider is successfully loaded
|
||||
* @return true if provider is loaded, false otherwise
|
||||
*/
|
||||
bool IsLoaded() const { return m_provider != nullptr; }
|
||||
|
||||
/**
|
||||
* @brief Get the version reported by the loaded provider
|
||||
* @return Provider version, or an empty string if unavailable
|
||||
*/
|
||||
std::string Version() const;
|
||||
|
||||
/**
|
||||
* @brief Get the underlying provider handle
|
||||
* @return OSSL_PROVIDER pointer or nullptr if not loaded
|
||||
*/
|
||||
OSSL_PROVIDER* Get() const { return m_provider; }
|
||||
|
||||
/**
|
||||
* @brief Move constructor
|
||||
*/
|
||||
OpenSSLProvider(OpenSSLProvider&& other) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Move assignment operator
|
||||
*/
|
||||
OpenSSLProvider& operator=(OpenSSLProvider&& other) noexcept;
|
||||
|
||||
// Delete copy operations to prevent double-free
|
||||
OpenSSLProvider(const OpenSSLProvider&) = delete;
|
||||
OpenSSLProvider& operator=(const OpenSSLProvider&) = delete;
|
||||
|
||||
private:
|
||||
OSSL_PROVIDER* m_provider; /**< OpenSSL provider handle */
|
||||
std::string m_providerName; /**< Provider name for logging */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief RAII wrapper for OpenSSL 3.x provider management
|
||||
*
|
||||
* This class manages both legacy and default providers for OpenSSL 3.x,
|
||||
* ensuring they are loaded and unloaded properly. It's designed to be
|
||||
* used at application startup to handle provider initialization.
|
||||
*/
|
||||
class OpenSSLProviderManager
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor - loads legacy and default providers
|
||||
*/
|
||||
OpenSSLProviderManager();
|
||||
|
||||
/**
|
||||
* @brief Destructor - automatically unloads providers
|
||||
*/
|
||||
~OpenSSLProviderManager();
|
||||
|
||||
/**
|
||||
* @brief The one manager for the process
|
||||
*
|
||||
* Providers are global to the library, so loading them per object costs an
|
||||
* OpenSSL-wide lock on every construction and buys nothing.
|
||||
*/
|
||||
static OpenSSLProviderManager& Instance();
|
||||
|
||||
/**
|
||||
* @brief Check if providers are successfully loaded
|
||||
* @return true if both providers loaded successfully
|
||||
*/
|
||||
bool IsInitialized() const { return m_initialized; }
|
||||
|
||||
/**
|
||||
* @brief Get legacy provider
|
||||
* @return Reference to legacy provider
|
||||
*/
|
||||
const OpenSSLProvider& GetLegacyProvider() const { return m_legacyProvider; }
|
||||
|
||||
/**
|
||||
* @brief Get default provider
|
||||
* @return Reference to default provider
|
||||
*/
|
||||
const OpenSSLProvider& GetDefaultProvider() const { return m_defaultProvider; }
|
||||
|
||||
private:
|
||||
OpenSSLProvider m_legacyProvider; /**< Legacy provider for compatibility */
|
||||
OpenSSLProvider m_defaultProvider; /**< Default provider */
|
||||
bool m_initialized; /**< Initialization status */
|
||||
};
|
||||
|
||||
#endif // OPENSSL_VERSION_MAJOR >= 3
|
||||
|
||||
#endif // _AUTH_OPENSSL_PROVIDER_H
|
||||
|
|
@ -30,8 +30,6 @@ set(SRC_GRP_AUTH
|
|||
Auth/HMACSHA1.cpp
|
||||
Auth/HMACSHA1.h
|
||||
Auth/Md5.h
|
||||
Auth/OpenSSLProvider.cpp
|
||||
Auth/OpenSSLProvider.h
|
||||
Auth/Sha1.cpp
|
||||
Auth/Sha1.h
|
||||
Auth/WardenKeyGeneration.h
|
||||
|
|
|
|||
|
|
@ -42,7 +42,6 @@ set(SRC_GRP_TESTS
|
|||
SessionMailboxTest.cpp
|
||||
SessionProtocolPolicyTest.cpp
|
||||
DatabaseConcurrencyTest.cpp
|
||||
OpenSSLProviderTest.cpp
|
||||
ClientParserTest.cpp
|
||||
TerrainModelTest.cpp
|
||||
TileSerializerTest.cpp
|
||||
|
|
@ -149,26 +148,7 @@ if(WIN32)
|
|||
"${MANGOS_TEST_MYSQL_DLL_DIR}/${MANGOS_TEST_MYSQL_DLL_NAME}"
|
||||
"$<TARGET_FILE_DIR:mangos_tests>")
|
||||
endif()
|
||||
set_tests_properties(mangos_tests PROPERTIES
|
||||
ENVIRONMENT "OPENSSL_MODULES=")
|
||||
|
||||
# Match the release layout and leave OPENSSL_MODULES empty: provider discovery
|
||||
# must work from ossl-modules beside the executable, not from the build host.
|
||||
find_file(MANGOS_TEST_OPENSSL_LEGACY_DLL
|
||||
NAMES legacy.dll
|
||||
HINTS "${MANGOS_SSL_ROOT}" "${OPENSSL_ROOT_DIR}"
|
||||
PATH_SUFFIXES bin/ossl-modules bin ossl-modules lib/ossl-modules ""
|
||||
NO_DEFAULT_PATH)
|
||||
|
||||
if(MANGOS_TEST_OPENSSL_LEGACY_DLL)
|
||||
add_custom_command(TARGET mangos_tests POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory
|
||||
"$<TARGET_FILE_DIR:mangos_tests>/ossl-modules"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${MANGOS_TEST_OPENSSL_LEGACY_DLL}"
|
||||
"$<TARGET_FILE_DIR:mangos_tests>/ossl-modules/legacy.dll")
|
||||
else()
|
||||
message(WARNING
|
||||
"OpenSSL legacy provider (legacy.dll) not found; mangos_tests may fail its RC4 cases.")
|
||||
endif()
|
||||
# No ossl-modules here, and no OPENSSL_MODULES to point at one. RC4 was the only
|
||||
# thing this tree ever asked the legacy provider for, and it is in the tree now, so
|
||||
# the tests need libcrypto and nothing beside it.
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -1,76 +0,0 @@
|
|||
/**
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* MaNGOS is a full featured server for World of Warcraft, supporting
|
||||
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
|
||||
*
|
||||
* Copyright (C) 2005-2026 MaNGOS <https://www.getmangos.eu>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
|
||||
* and lore are copyrighted by Blizzard Entertainment, Inc.
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include "TestHarness.h"
|
||||
#include "Auth/OpenSSLProvider.h"
|
||||
|
||||
#include <charconv>
|
||||
#include <cstdlib>
|
||||
#include <openssl/crypto.h>
|
||||
#include <openssl/evp.h>
|
||||
|
||||
TEST(OpenSSL_runtime_and_providers_are_major_three)
|
||||
{
|
||||
const char* modules = std::getenv("OPENSSL_MODULES");
|
||||
std::string modulesBefore = modules ? modules : "";
|
||||
|
||||
OpenSSLProviderManager& manager =
|
||||
OpenSSLProviderManager::Instance();
|
||||
|
||||
const char* modulesAfter = std::getenv("OPENSSL_MODULES");
|
||||
CHECK_STR(std::string(modulesAfter ? modulesAfter : ""), modulesBefore);
|
||||
REQUIRE(manager.IsInitialized());
|
||||
|
||||
unsigned runtimeMajor =
|
||||
unsigned((OpenSSL_version_num() >> 28) & 0x0f);
|
||||
CHECK_EQ(runtimeMajor, 3);
|
||||
|
||||
auto checkProvider = [runtimeMajor](
|
||||
const OpenSSLProvider& provider)
|
||||
{
|
||||
std::string version = provider.Version();
|
||||
REQUIRE(!version.empty());
|
||||
std::size_t separator = version.find('.');
|
||||
REQUIRE(separator != std::string::npos);
|
||||
unsigned major = 0;
|
||||
std::from_chars_result result = std::from_chars(
|
||||
version.data(), version.data() + separator, major);
|
||||
CHECK(result.ec == std::errc{});
|
||||
CHECK(result.ptr == version.data() + separator);
|
||||
CHECK_EQ(major, runtimeMajor);
|
||||
};
|
||||
|
||||
checkProvider(manager.GetLegacyProvider());
|
||||
checkProvider(manager.GetDefaultProvider());
|
||||
}
|
||||
|
||||
TEST(OpenSSL_legacy_provider_supplies_rc4)
|
||||
{
|
||||
REQUIRE(OpenSSLProviderManager::Instance().IsInitialized());
|
||||
EVP_CIPHER* rc4 = EVP_CIPHER_fetch(nullptr, "RC4", nullptr);
|
||||
CHECK(rc4 != nullptr);
|
||||
EVP_CIPHER_free(rc4);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue