Skip to content

Commit

Permalink
[release/8.0]: Light up support for OpenSSL ENGINEs only if they are …
Browse files Browse the repository at this point in the history
…available.

Some Linux distributions are phasing out support for OpenSSL 1.x ENGINEs. They are doing this by moving the header, `engine.h`, to a separate package or removing the header entirely. The actual OpenSSL shared libraries still contain the engine APIs. This makes the change an API, not ABI, break.

We react to this by disabling OpenSSL engine support on non-portable builds that are missing the engine header. Portable builds will continue to probe the loaded OpenSSL library for support, and non-portable builds will only support ENGINEs if the header is present.
  • Loading branch information
vcsjones authored Jul 24, 2024
1 parent 9b9f7ed commit 07c469f
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,8 @@ internal static ArraySegment<byte> RentEncodeSubjectPublicKeyInfo(SafeEvpPKeyHan
[LibraryImport(Libraries.CryptoNative, StringMarshalling = StringMarshalling.Utf8)]
private static partial SafeEvpPKeyHandle CryptoNative_LoadPrivateKeyFromEngine(
string engineName,
string keyName);
string keyName,
[MarshalAs(UnmanagedType.Bool)] out bool haveEngine);

internal static SafeEvpPKeyHandle LoadPrivateKeyFromEngine(
string engineName,
Expand All @@ -226,7 +227,13 @@ internal static SafeEvpPKeyHandle LoadPrivateKeyFromEngine(
Debug.Assert(engineName is not null);
Debug.Assert(keyName is not null);

SafeEvpPKeyHandle pkey = CryptoNative_LoadPrivateKeyFromEngine(engineName, keyName);
SafeEvpPKeyHandle pkey = CryptoNative_LoadPrivateKeyFromEngine(engineName, keyName, out bool haveEngine);

if (!haveEngine)
{
pkey.Dispose();
throw new CryptographicException(SR.Cryptography_EnginesNotSupported);
}

if (pkey.IsInvalid)
{
Expand All @@ -240,7 +247,8 @@ internal static SafeEvpPKeyHandle LoadPrivateKeyFromEngine(
[LibraryImport(Libraries.CryptoNative, StringMarshalling = StringMarshalling.Utf8)]
private static partial SafeEvpPKeyHandle CryptoNative_LoadPublicKeyFromEngine(
string engineName,
string keyName);
string keyName,
[MarshalAs(UnmanagedType.Bool)] out bool haveEngine);

internal static SafeEvpPKeyHandle LoadPublicKeyFromEngine(
string engineName,
Expand All @@ -249,7 +257,13 @@ internal static SafeEvpPKeyHandle LoadPublicKeyFromEngine(
Debug.Assert(engineName is not null);
Debug.Assert(keyName is not null);

SafeEvpPKeyHandle pkey = CryptoNative_LoadPublicKeyFromEngine(engineName, keyName);
SafeEvpPKeyHandle pkey = CryptoNative_LoadPublicKeyFromEngine(engineName, keyName, out bool haveEngine);

if (!haveEngine)
{
pkey.Dispose();
throw new CryptographicException(SR.Cryptography_EnginesNotSupported);
}

if (pkey.IsInvalid)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,9 @@
<data name="Cryptography_EncryptedIncorrectLength" xml:space="preserve">
<value>{0} unexpectedly produced a ciphertext with the incorrect length.</value>
</data>
<data name="Cryptography_EnginesNotSupported" xml:space="preserve">
<value>OpenSSL ENGINE is not available on this platform.</value>
</data>
<data name="Cryptography_ExceedKdfExtractLimit" xml:space="preserve">
<value>The total number of bytes extracted cannot exceed UInt32.MaxValue * hash length.</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
include(CheckLibraryExists)
include(CheckFunctionExists)
include(CheckSourceCompiles)

set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY} ${OPENSSL_SSL_LIBRARY})
set(CMAKE_REQUIRED_DEFINITIONS -DOPENSSL_API_COMPAT=0x10100000L)

check_function_exists(
EC_GF2m_simple_method
Expand All @@ -22,6 +24,11 @@ check_function_exists(
HAVE_OPENSSL_SHA3
)

check_source_compiles(C "
#include <openssl/engine.h>
int main(void) { ENGINE_init(NULL); return 1; }"
HAVE_OPENSSL_ENGINE)

configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/pal_crypto_config.h.in
${CMAKE_CURRENT_BINARY_DIR}/pal_crypto_config.h)
36 changes: 29 additions & 7 deletions src/native/libs/System.Security.Cryptography.Native/opensslshim.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
#include <openssl/dsa.h>
#include <openssl/ec.h>
#include <openssl/ecdsa.h>
#include <openssl/engine.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
Expand Down Expand Up @@ -46,6 +45,11 @@
#include <openssl/provider.h>
#endif

#if HAVE_OPENSSL_ENGINE
// Some Linux distributions build without engine support.
#include <openssl/engine.h>
#endif

#if OPENSSL_VERSION_NUMBER >= OPENSSL_VERSION_1_1_1_RTM
#define HAVE_OPENSSL_SET_CIPHERSUITES 1
#else
Expand Down Expand Up @@ -168,6 +172,24 @@ const EVP_MD *EVP_shake256(void);
int EVP_DigestFinalXOF(EVP_MD_CTX *ctx, unsigned char *md, size_t len);
#endif

#if !HAVE_OPENSSL_ENGINE
#undef HAVE_OPENSSL_ENGINE
#define HAVE_OPENSSL_ENGINE 1

ENGINE *ENGINE_by_id(const char *id);
int ENGINE_init(ENGINE *e);
int ENGINE_finish(ENGINE *e);
ENGINE *ENGINE_new(void);
int ENGINE_free(ENGINE *e);
typedef EVP_PKEY *(*ENGINE_LOAD_KEY_PTR)(ENGINE *, const char *,
UI_METHOD *ui_method,
void *callback_data);
EVP_PKEY *ENGINE_load_private_key(ENGINE *e, const char *key_id,
UI_METHOD *ui_method, void *callback_data);
EVP_PKEY *ENGINE_load_public_key(ENGINE *e, const char *key_id,
UI_METHOD *ui_method, void *callback_data);
#endif

#define API_EXISTS(fn) (fn != NULL)

// List of all functions from the libssl that are used in the System.Security.Cryptography.Native.
Expand Down Expand Up @@ -298,12 +320,12 @@ int EVP_DigestFinalXOF(EVP_MD_CTX *ctx, unsigned char *md, size_t len);
REQUIRED_FUNCTION(EC_POINT_mul) \
REQUIRED_FUNCTION(EC_POINT_new) \
REQUIRED_FUNCTION(EC_POINT_set_affine_coordinates_GFp) \
REQUIRED_FUNCTION(ENGINE_by_id) \
REQUIRED_FUNCTION(ENGINE_finish) \
REQUIRED_FUNCTION(ENGINE_free) \
REQUIRED_FUNCTION(ENGINE_init) \
REQUIRED_FUNCTION(ENGINE_load_public_key) \
REQUIRED_FUNCTION(ENGINE_load_private_key) \
LIGHTUP_FUNCTION(ENGINE_by_id) \
LIGHTUP_FUNCTION(ENGINE_finish) \
LIGHTUP_FUNCTION(ENGINE_free) \
LIGHTUP_FUNCTION(ENGINE_init) \
LIGHTUP_FUNCTION(ENGINE_load_public_key) \
LIGHTUP_FUNCTION(ENGINE_load_private_key) \
REQUIRED_FUNCTION(ERR_clear_error) \
REQUIRED_FUNCTION(ERR_error_string_n) \
REQUIRED_FUNCTION(ERR_get_error) \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
#cmakedefine01 HAVE_OPENSSL_ALPN
#cmakedefine01 HAVE_OPENSSL_CHACHA20POLY1305
#cmakedefine01 HAVE_OPENSSL_SHA3
#cmakedefine01 HAVE_OPENSSL_ENGINE
65 changes: 48 additions & 17 deletions src/native/libs/System.Security.Cryptography.Native/pal_evp_pkey.c
Original file line number Diff line number Diff line change
Expand Up @@ -511,41 +511,72 @@ int32_t CryptoNative_EncodeSubjectPublicKeyInfo(EVP_PKEY* pkey, uint8_t* buf)
return i2d_PUBKEY(pkey, &buf);
}

#if HAVE_OPENSSL_ENGINE
static EVP_PKEY* LoadKeyFromEngine(
const char* engineName,
const char* keyName,
ENGINE_LOAD_KEY_PTR load_func)
ENGINE_LOAD_KEY_PTR load_func,
int32_t* haveEngine)
{
assert(haveEngine);
ERR_clear_error();

EVP_PKEY* ret = NULL;
ENGINE* engine = NULL;
if (API_EXISTS(ENGINE_by_id) && API_EXISTS(ENGINE_init) && API_EXISTS(ENGINE_finish) && API_EXISTS(ENGINE_free))
{
*haveEngine = 1;
EVP_PKEY* ret = NULL;
ENGINE* engine = NULL;

// Per https://github.com/openssl/openssl/discussions/21427
// using EVP_PKEY after freeing ENGINE is correct.
engine = ENGINE_by_id(engineName);
// Per https://github.com/openssl/openssl/discussions/21427
// using EVP_PKEY after freeing ENGINE is correct.
engine = ENGINE_by_id(engineName);

if (engine != NULL)
{
if (ENGINE_init(engine))
if (engine != NULL)
{
ret = load_func(engine, keyName, NULL, NULL);
if (ENGINE_init(engine))
{
ret = load_func(engine, keyName, NULL, NULL);

ENGINE_finish(engine);
}

ENGINE_finish(engine);
ENGINE_free(engine);
}

ENGINE_free(engine);
return ret;
}

return ret;
*haveEngine = 0;
return NULL;
}
#endif

EVP_PKEY* CryptoNative_LoadPrivateKeyFromEngine(const char* engineName, const char* keyName)
EVP_PKEY* CryptoNative_LoadPrivateKeyFromEngine(const char* engineName, const char* keyName, int32_t* haveEngine)
{
return LoadKeyFromEngine(engineName, keyName, ENGINE_load_private_key);
#if HAVE_OPENSSL_ENGINE
if (API_EXISTS(ENGINE_load_private_key))
{
return LoadKeyFromEngine(engineName, keyName, ENGINE_load_private_key, haveEngine);
}
#endif
(void)engineName;
(void)keyName;
(void)haveEngine;
*haveEngine = 0;
return NULL;
}

EVP_PKEY* CryptoNative_LoadPublicKeyFromEngine(const char* engineName, const char* keyName)
EVP_PKEY* CryptoNative_LoadPublicKeyFromEngine(const char* engineName, const char* keyName, int32_t* haveEngine)
{
return LoadKeyFromEngine(engineName, keyName, ENGINE_load_public_key);
#if HAVE_OPENSSL_ENGINE
if (API_EXISTS(ENGINE_load_private_key))
{
return LoadKeyFromEngine(engineName, keyName, ENGINE_load_public_key, haveEngine);
}
#endif
(void)engineName;
(void)keyName;
(void)haveEngine;
*haveEngine = 0;
return NULL;
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,14 @@ PALEXPORT int32_t CryptoNative_EncodeSubjectPublicKeyInfo(EVP_PKEY* pkey, uint8_
Load a named key, via ENGINE_load_private_key, from the named engine.
Returns a valid EVP_PKEY* on success, NULL on failure.
haveEngine is 1 if OpenSSL ENGINE's are supported, otherwise 0.
*/
PALEXPORT EVP_PKEY* CryptoNative_LoadPrivateKeyFromEngine(const char* engineName, const char* keyName);
PALEXPORT EVP_PKEY* CryptoNative_LoadPrivateKeyFromEngine(const char* engineName, const char* keyName, int32_t* haveEngine);

/*
Load a named key, via ENGINE_load_public_key, from the named engine.
Returns a valid EVP_PKEY* on success, NULL on failure.
haveEngine is 1 if OpenSSL ENGINE's are supported, otherwise 0.
*/
PALEXPORT EVP_PKEY* CryptoNative_LoadPublicKeyFromEngine(const char* engineName, const char* keyName);
PALEXPORT EVP_PKEY* CryptoNative_LoadPublicKeyFromEngine(const char* engineName, const char* keyName, int32_t* haveEngine);

0 comments on commit 07c469f

Please sign in to comment.