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

openSUSE Build Service is sponsored by