File 8781-crypto-Avoid-resource-takeovers-between-incompatible.patch of Package erlang
From 8f37cca08c18916806df4f5d33509177c1ee7c36 Mon Sep 17 00:00:00 2001
From: Sverker Eriksson <sverker@erlang.org>
Date: Tue, 19 Sep 2023 14:34:51 +0200
Subject: [PATCH 1/3] crypto: Avoid resource takeovers between incompatible
OpenSSL versions
---
lib/crypto/c_src/cipher.c | 6 +-
lib/crypto/c_src/cipher.h | 2 +-
lib/crypto/c_src/crypto.c | 126 ++++++++++++++++++++++++--------------
lib/crypto/c_src/engine.c | 12 ++--
lib/crypto/c_src/engine.h | 2 +-
lib/crypto/c_src/hash.c | 6 +-
lib/crypto/c_src/hash.h | 2 +-
lib/crypto/c_src/hmac.c | 6 +-
lib/crypto/c_src/hmac.h | 2 +-
lib/crypto/c_src/info.c | 17 +++++
lib/crypto/c_src/info.h | 12 ++++
lib/crypto/c_src/mac.c | 8 +--
lib/crypto/c_src/mac.h | 2 +-
13 files changed, 136 insertions(+), 67 deletions(-)
diff --git a/lib/crypto/c_src/cipher.c b/lib/crypto/c_src/cipher.c
index 30c0fbcdb7..30f4c595ff 100644
--- a/lib/crypto/c_src/cipher.c
+++ b/lib/crypto/c_src/cipher.c
@@ -19,6 +19,7 @@
*/
#include "cipher.h"
+#include "info.h"
#define NOT_AEAD {{0,0,0}}
#define AEAD_CTRL {{EVP_CTRL_AEAD_SET_IVLEN,EVP_CTRL_AEAD_GET_TAG,EVP_CTRL_AEAD_SET_TAG}}
@@ -164,8 +165,9 @@ static void evp_cipher_ctx_dtor(ErlNifEnv* env, struct evp_cipher_ctx* ctx) {
#endif
}
-int init_cipher_ctx(ErlNifEnv *env) {
- evp_cipher_ctx_rtype = enif_open_resource_type(env, NULL, "EVP_CIPHER_CTX",
+int init_cipher_ctx(ErlNifEnv *env, ErlNifBinary* rt_buf) {
+ evp_cipher_ctx_rtype = enif_open_resource_type(env, NULL,
+ resource_name("EVP_CIPHER_CTX", rt_buf),
(ErlNifResourceDtor*) evp_cipher_ctx_dtor,
ERL_NIF_RT_CREATE|ERL_NIF_RT_TAKEOVER,
NULL);
diff --git a/lib/crypto/c_src/cipher.h b/lib/crypto/c_src/cipher.h
index 82b7ef4d20..71a05738ce 100644
--- a/lib/crypto/c_src/cipher.h
+++ b/lib/crypto/c_src/cipher.h
@@ -73,7 +73,7 @@ struct evp_cipher_ctx {
ERL_NIF_TERM cipher_info_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
-int init_cipher_ctx(ErlNifEnv *env);
+int init_cipher_ctx(ErlNifEnv *env, ErlNifBinary* rt_buf);
void init_cipher_types(ErlNifEnv* env);
const struct cipher_type_t* get_cipher_type_no_key(ERL_NIF_TERM type);
diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c
index 2a6d5e9d4c..30ee7379ac 100644
--- a/lib/crypto/c_src/crypto.c
+++ b/lib/crypto/c_src/crypto.c
@@ -176,84 +176,107 @@ static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info)
int tpl_arity;
const ERL_NIF_TERM* tpl_array;
int vernum;
+ ErlNifBinary rt_buf = { 0, NULL };
ErlNifBinary lib_bin;
#ifdef HAVE_DYNAMIC_CRYPTO_LIB
char lib_buf[1000];
void *handle;
#endif
+ int ret = -1;
- if (!verify_lib_version())
- return __LINE__;
-
+ if (!verify_lib_version()) {
+ ret = __LINE__; goto done;
+ }
/* load_info: {302, <<"/full/path/of/this/library">>,true|false} */
- if (!enif_get_tuple(env, load_info, &tpl_arity, &tpl_array))
- return __LINE__;
- if (tpl_arity != 3)
- return __LINE__;
- if (!enif_get_int(env, tpl_array[0], &vernum))
- return __LINE__;
- if (vernum != 302)
- return __LINE__;
- if (!enif_inspect_binary(env, tpl_array[1], &lib_bin))
- return __LINE__;
+ if (!enif_get_tuple(env, load_info, &tpl_arity, &tpl_array)) {
+ ret = __LINE__; goto done;
+ }
+ if (tpl_arity != 3) {
+ ret = __LINE__; goto done;
+ }
+ if (!enif_get_int(env, tpl_array[0], &vernum)) {
+ ret = __LINE__; goto done;
+ }
+ if (vernum != 302) {
+ ret = __LINE__; goto done;
+ }
+ if (!enif_inspect_binary(env, tpl_array[1], &lib_bin)) {
+ ret = __LINE__; goto done;
+ }
+ if (!enif_alloc_binary(100, &rt_buf)) {
+ ret = __LINE__; goto done;
+ }
#ifdef HAS_EVP_PKEY_CTX
- if (!init_mac_ctx(env)) {
- return __LINE__;
+ if (!init_mac_ctx(env, &rt_buf)) {
+ ret = __LINE__; goto done;
}
#else
- if (!init_hmac_ctx(env)) {
- return __LINE__;
+ if (!init_hmac_ctx(env, &rt_buf)) {
+ ret = __LINE__; goto done;
}
#endif
- if (!init_hash_ctx(env)) {
- return __LINE__;
+ if (!init_hash_ctx(env, &rt_buf)) {
+ ret = __LINE__; goto done;
}
- if (!init_cipher_ctx(env)) {
- return __LINE__;
+ if (!init_cipher_ctx(env, &rt_buf)) {
+ ret = __LINE__; goto done;
}
- if (!init_engine_ctx(env)) {
- return __LINE__;
+ if (!init_engine_ctx(env, &rt_buf)) {
+ ret = __LINE__; goto done;
}
if (!create_engine_mutex(env)) {
- return __LINE__;
+ ret = __LINE__; goto done;
+ }
+ if (!create_curve_mutex()) {
+ ret = __LINE__; goto done;
}
- if (!create_curve_mutex())
- return __LINE__;
#ifdef HAS_3_0_API
prov_cnt = 0;
# ifdef FIPS_SUPPORT
- if ((prov_cnt<MAX_NUM_PROVIDERS) && !(prov[prov_cnt++] = OSSL_PROVIDER_load(NULL, "fips"))) return __LINE__;
-#endif
- if ((prov_cnt<MAX_NUM_PROVIDERS) && !(prov[prov_cnt++] = OSSL_PROVIDER_load(NULL, "default"))) return __LINE__;
- if ((prov_cnt<MAX_NUM_PROVIDERS) && !(prov[prov_cnt++] = OSSL_PROVIDER_load(NULL, "base"))) return __LINE__;
- if (prov_cnt<MAX_NUM_PROVIDERS) {prov_cnt++; OSSL_PROVIDER_load(NULL, "legacy");}
+ if (!(prov[prov_cnt++] = OSSL_PROVIDER_load(NULL, "fips"))) {
+ ret = __LINE__; goto done;
+ }
+# endif
+ if (!(prov[prov_cnt++] = OSSL_PROVIDER_load(NULL, "default"))) {
+ ret = __LINE__; goto done;
+ }
+ if (!(prov[prov_cnt++] = OSSL_PROVIDER_load(NULL, "base"))) {
+ ret = __LINE__; goto done;
+ }
+ if ((prov[prov_cnt] = OSSL_PROVIDER_load(NULL, "legacy"))) {
+ /* Don't fail loading if the legacy provider is missing */
+ prov_cnt++;
+ }
#endif
if (library_initialized) {
/* Repeated loading of this library (module upgrade).
* Atoms and callbacks are already set, we are done.
*/
- return 0;
+ ret = 0;
+ goto done;
}
if (!init_atoms(env)) {
- return __LINE__;
+ ret = __LINE__; goto done;
}
-
/* Check if enter FIPS mode at module load (happening now) */
- if (enable_fips_mode(env, tpl_array[2]) != atom_true)
- return __LINE__;
-
+ if (enable_fips_mode(env, tpl_array[2]) != atom_true) {
+ ret = __LINE__; goto done;
+ }
#ifdef HAVE_DYNAMIC_CRYPTO_LIB
- if (!change_basename(&lib_bin, lib_buf, sizeof(lib_buf), crypto_callback_name))
- return __LINE__;
- if ((handle = enif_dlopen(lib_buf, &error_handler, NULL)) == NULL)
- return __LINE__;
+ if (!change_basename(&lib_bin, lib_buf, sizeof(lib_buf), crypto_callback_name)) {
+ ret = __LINE__; goto done;
+ }
+ if ((handle = enif_dlopen(lib_buf, &error_handler, NULL)) == NULL) {
+ ret = __LINE__; goto done;
+ }
if ((funcp = (get_crypto_callbacks_t*) enif_dlsym(handle, "get_crypto_callbacks",
- &error_handler, NULL)) == NULL)
- return __LINE__;
+ &error_handler, NULL)) == NULL) {
+ ret = __LINE__; goto done;
+ }
#else /* !HAVE_DYNAMIC_CRYPTO_LIB */
funcp = &get_crypto_callbacks;
#endif
@@ -272,12 +295,13 @@ static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info)
if (!ccb || ccb->sizeof_me != sizeof(*ccb)) {
PRINTF_ERR0("Invalid 'crypto_callbacks'");
- return __LINE__;
+ ret = __LINE__; goto done;
}
#ifdef HAS_CRYPTO_MEM_FUNCTIONS
- if (!CRYPTO_set_mem_functions(ccb->crypto_alloc, ccb->crypto_realloc, ccb->crypto_free))
- return __LINE__;
+ if (!CRYPTO_set_mem_functions(ccb->crypto_alloc, ccb->crypto_realloc, ccb->crypto_free)) {
+ ret = __LINE__; goto done;
+ }
#endif
#if OPENSSL_VERSION_NUMBER < PACKED_OPENSSL_VERSION_PLAIN(1,1,0)
@@ -299,7 +323,15 @@ static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info)
init_algorithms_types(env);
library_initialized = 1;
- return 0;
+ ret = 0;
+
+done:
+ ASSERT(ret >= 0);
+
+ if (rt_buf.data)
+ enif_release_binary(&rt_buf);
+
+ return ret;
}
static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
diff --git a/lib/crypto/c_src/engine.c b/lib/crypto/c_src/engine.c
index 6fb195b813..ff35ca614e 100644
--- a/lib/crypto/c_src/engine.c
+++ b/lib/crypto/c_src/engine.c
@@ -19,6 +19,7 @@
*/
#include "engine.h"
+#include "info.h"
#ifdef HAS_ENGINE_SUPPORT
struct engine_ctx {
@@ -110,12 +111,13 @@ static int zero_terminate(ErlNifBinary bin, char **buf) {
}
#endif /* HAS_ENGINE_SUPPORT */
-int init_engine_ctx(ErlNifEnv *env) {
+int init_engine_ctx(ErlNifEnv *env, ErlNifBinary* rt_buf) {
#ifdef HAS_ENGINE_SUPPORT
- engine_ctx_rtype = enif_open_resource_type(env, NULL, "ENGINE_CTX",
- (ErlNifResourceDtor*) engine_ctx_dtor,
- ERL_NIF_RT_CREATE|ERL_NIF_RT_TAKEOVER,
- NULL);
+ engine_ctx_rtype = enif_open_resource_type(env, NULL,
+ resource_name("ENGINE_CTX", rt_buf),
+ (ErlNifResourceDtor*) engine_ctx_dtor,
+ ERL_NIF_RT_CREATE|ERL_NIF_RT_TAKEOVER,
+ NULL);
if (engine_ctx_rtype == NULL) {
PRINTF_ERR0("CRYPTO: Could not open resource type 'ENGINE_CTX'");
return 0;
diff --git a/lib/crypto/c_src/engine.h b/lib/crypto/c_src/engine.h
index f6a5a2921b..b1966b4f3d 100644
--- a/lib/crypto/c_src/engine.h
+++ b/lib/crypto/c_src/engine.h
@@ -28,7 +28,7 @@ int get_engine_and_key_id(ErlNifEnv *env, ERL_NIF_TERM key, char ** id, ENGINE *
char *get_key_password(ErlNifEnv *env, ERL_NIF_TERM key);
#endif /* HAS_ENGINE_SUPPORT */
-int init_engine_ctx(ErlNifEnv *env);
+int init_engine_ctx(ErlNifEnv *env, ErlNifBinary* rt_buf);
int create_engine_mutex(ErlNifEnv *env);
void destroy_engine_mutex(ErlNifEnv *env);
diff --git a/lib/crypto/c_src/hash.c b/lib/crypto/c_src/hash.c
index 029dffd44b..e80f2a2522 100644
--- a/lib/crypto/c_src/hash.c
+++ b/lib/crypto/c_src/hash.c
@@ -20,6 +20,7 @@
#include "hash.h"
#include "digest.h"
+#include "info.h"
#ifdef HAVE_MD5
# define MD5_CTX_LEN (sizeof(MD5_CTX))
@@ -48,9 +49,10 @@ static void evp_md_ctx_dtor(ErlNifEnv* env, struct evp_md_ctx *ctx) {
}
#endif
-int init_hash_ctx(ErlNifEnv* env) {
+int init_hash_ctx(ErlNifEnv* env, ErlNifBinary* rt_buf) {
#if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,0,0)
- evp_md_ctx_rtype = enif_open_resource_type(env, NULL, "EVP_MD_CTX",
+ evp_md_ctx_rtype = enif_open_resource_type(env, NULL,
+ resource_name("EVP_MD_CTX", rt_buf),
(ErlNifResourceDtor*) evp_md_ctx_dtor,
ERL_NIF_RT_CREATE|ERL_NIF_RT_TAKEOVER,
NULL);
diff --git a/lib/crypto/c_src/hash.h b/lib/crypto/c_src/hash.h
index 5ac6785a33..093410ed97 100644
--- a/lib/crypto/c_src/hash.h
+++ b/lib/crypto/c_src/hash.h
@@ -23,7 +23,7 @@
#include "common.h"
-int init_hash_ctx(ErlNifEnv *env);
+int init_hash_ctx(ErlNifEnv *env, ErlNifBinary* rt_buf);
ERL_NIF_TERM hash_info_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM hash_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
diff --git a/lib/crypto/c_src/hmac.c b/lib/crypto/c_src/hmac.c
index 86ffb9a5a3..1244069aed 100644
--- a/lib/crypto/c_src/hmac.c
+++ b/lib/crypto/c_src/hmac.c
@@ -30,6 +30,7 @@
#include "hmac.h"
#include "digest.h"
+#include "info.h"
#if !defined(HAS_EVP_PKEY_CTX) || DISABLE_EVP_HMAC
@@ -44,8 +45,9 @@ static ErlNifResourceType* hmac_context_rtype;
static void hmac_context_dtor(ErlNifEnv* env, struct hmac_context*);
-int init_hmac_ctx(ErlNifEnv *env) {
- hmac_context_rtype = enif_open_resource_type(env, NULL, "hmac_context",
+int init_hmac_ctx(ErlNifEnv *env, ErlNifBinary* rt_buf) {
+ hmac_context_rtype = enif_open_resource_type(env, NULL,
+ resource_name("hmac_context", rt_buf),
(ErlNifResourceDtor*) hmac_context_dtor,
ERL_NIF_RT_CREATE|ERL_NIF_RT_TAKEOVER,
NULL);
diff --git a/lib/crypto/c_src/hmac.h b/lib/crypto/c_src/hmac.h
index 1948ae5b84..f84d08dd38 100644
--- a/lib/crypto/c_src/hmac.h
+++ b/lib/crypto/c_src/hmac.h
@@ -25,7 +25,7 @@
#if !defined(HAS_EVP_PKEY_CTX) || DISABLE_EVP_HMAC
-int init_hmac_ctx(ErlNifEnv *env);
+int init_hmac_ctx(ErlNifEnv *env, ErlNifBinary* rt_buf);
ERL_NIF_TERM hmac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM hmac_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
diff --git a/lib/crypto/c_src/info.c b/lib/crypto/c_src/info.c
index 1458842b98..f78984b7e6 100644
--- a/lib/crypto/c_src/info.c
+++ b/lib/crypto/c_src/info.c
@@ -87,6 +87,23 @@ void error_handler(void* null, const char* errstr)
}
#endif /* HAVE_DYNAMIC_CRYPTO_LIB */
+const char* resource_name(const char *name, ErlNifBinary* buf)
+{
+ /*
+ * Add full OpenSSL version string. This is a simlpe but conservative way
+ * to detect and reject resource takover between different versions
+ * of OpenSSL that might not be binary compatible.
+ */
+ size_t len;
+ for (;;) {
+ len = enif_snprintf((char*)buf->data, buf->size, "%s:%s",
+ name, OpenSSL_version(OPENSSL_VERSION));
+ if (len < buf->size)
+ return (char*)buf->data;
+ enif_realloc_binary(buf, len + 1 + 20);
+ }
+}
+
ERL_NIF_TERM info_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
{/* () */
diff --git a/lib/crypto/c_src/info.h b/lib/crypto/c_src/info.h
index fd9e9b7374..2ba2583e94 100644
--- a/lib/crypto/c_src/info.h
+++ b/lib/crypto/c_src/info.h
@@ -30,6 +30,18 @@ int change_basename(ErlNifBinary* bin, char* buf, size_t bufsz, const char* newf
void error_handler(void* null, const char* errstr);
#endif
+/** @brief Construct a versioned name for resource types to try
+ * avoid takeover of binary incompatible resources.
+ * @param[in] name The base name of the resource type.
+ * @param[out] buf Output buffer preallocated with enif_alloc_binary.
+ *
+ * @return Pointer to versioned name at buf->data, potentially reallocated.
+ *
+ * </>>
+ * </>>
+ */
+const char* resource_name(const char *name, ErlNifBinary* buf);
+
ERL_NIF_TERM info_lib(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM info_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
diff --git a/lib/crypto/c_src/mac.c b/lib/crypto/c_src/mac.c
index 4223f0567f..69d8be5350 100644
--- a/lib/crypto/c_src/mac.c
+++ b/lib/crypto/c_src/mac.c
@@ -24,6 +24,7 @@
#include "cmac.h"
#include "hmac.h"
#include "mac.h"
+#include "info.h"
/***************************
MAC type declaration
@@ -493,8 +494,6 @@ ERL_NIF_TERM mac_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
*
******************************************************************/
-int init_mac_ctx(ErlNifEnv *env);
-
struct mac_context
{
EVP_MD_CTX *ctx;
@@ -508,8 +507,9 @@ static ErlNifResourceType* mac_context_rtype;
static void mac_context_dtor(ErlNifEnv* env, struct mac_context*);
-int init_mac_ctx(ErlNifEnv *env) {
- mac_context_rtype = enif_open_resource_type(env, NULL, "mac_context",
+int init_mac_ctx(ErlNifEnv *env, ErlNifBinary* rt_buf) {
+ mac_context_rtype = enif_open_resource_type(env, NULL,
+ resource_name("mac_context", rt_buf),
(ErlNifResourceDtor*) mac_context_dtor,
ERL_NIF_RT_CREATE|ERL_NIF_RT_TAKEOVER,
NULL);
diff --git a/lib/crypto/c_src/mac.h b/lib/crypto/c_src/mac.h
index 46f8f5d292..87f9ff7938 100644
--- a/lib/crypto/c_src/mac.h
+++ b/lib/crypto/c_src/mac.h
@@ -23,7 +23,7 @@
#include "common.h"
-int init_mac_ctx(ErlNifEnv *env);
+int init_mac_ctx(ErlNifEnv *env, ErlNifBinary* rt_buf);
void init_mac_types(ErlNifEnv* env);
--
2.35.3