This change adds a new ENGINE called "dynamic" that allows new ENGINE
authorGeoff Thorpe <geoff@openssl.org>
Mon, 3 Sep 2001 19:15:29 +0000 (19:15 +0000)
committerGeoff Thorpe <geoff@openssl.org>
Mon, 3 Sep 2001 19:15:29 +0000 (19:15 +0000)
implementations to be loaded from self-contained shared-libraries. It also
provides (in engine.h) definitions and macros to help implement a
self-contained ENGINE. Version control is handled in a way whereby the
loader or loadee can veto the load depending on any objections it has with
each other's declared interface level. The way this is currently
implemented assumes a veto will only take place when one side notices the
other's interface level is too *old*. If the other side is newer, it should
be assumed the newer version knows better whether to veto the load or not.
Version checking (like other "dynamic" settings) can be controlled using
the "dynamic" ENGINE's control commands. Also, the semantics for the
loading allow a shared-library ENGINE implementation to handle differing
interface levels on the fly (eg. loading secondary shared-libraries
depending on the versions required).

Code will be added soon to the existing ENGINEs to illustrate how they can
be built as external libraries rather than building statically into
libcrypto.

NB: Applications wanting to support "dynamic"-loadable ENGINEs will need to
add support for ENGINE "control commands". See apps/engine.c for an example
of this, and use "apps/openssl engine -vvvv" to test or experiment.

crypto/engine/Makefile.ssl
crypto/engine/engine.h
crypto/engine/engine_dyn.c [new file with mode: 0644]
crypto/engine/engine_err.c
crypto/engine/engine_int.h
crypto/engine/engine_list.c

index abb8281..df209e9 100644 (file)
@@ -24,11 +24,11 @@ APPS=
 
 LIB=$(TOP)/libcrypto.a
 LIBSRC= engine_err.c engine_lib.c engine_list.c engine_all.c engine_openssl.c \
-       engine_evp.c \
+       engine_dyn.c engine_evp.c \
        hw_atalla.c hw_cswift.c hw_ncipher.c hw_nuron.c hw_ubsec.c \
        hw_openbsd_dev_crypto.c
 LIBOBJ= engine_err.o engine_lib.o engine_list.o engine_all.o engine_openssl.o \
-       engine_evp.o \
+       engine_dyn.o engine_evp.o \
        hw_atalla.o hw_cswift.o hw_ncipher.o hw_nuron.o hw_ubsec.o \
        hw_openbsd_dev_crypto.o
 
@@ -96,6 +96,18 @@ engine_all.o: ../../include/openssl/rsa.h ../../include/openssl/safestack.h
 engine_all.o: ../../include/openssl/stack.h ../../include/openssl/symhacks.h
 engine_all.o: ../../include/openssl/types.h ../../include/openssl/ui.h
 engine_all.o: engine_all.c engine_int.h
+engine_dyn.o: ../../e_os.h ../../include/openssl/asn1.h
+engine_dyn.o: ../../include/openssl/bio.h ../../include/openssl/bn.h
+engine_dyn.o: ../../include/openssl/buffer.h ../../include/openssl/crypto.h
+engine_dyn.o: ../../include/openssl/dh.h ../../include/openssl/dsa.h
+engine_dyn.o: ../../include/openssl/dso.h ../../include/openssl/e_os2.h
+engine_dyn.o: ../../include/openssl/engine.h ../../include/openssl/err.h
+engine_dyn.o: ../../include/openssl/lhash.h ../../include/openssl/opensslconf.h
+engine_dyn.o: ../../include/openssl/opensslv.h ../../include/openssl/rand.h
+engine_dyn.o: ../../include/openssl/rsa.h ../../include/openssl/safestack.h
+engine_dyn.o: ../../include/openssl/stack.h ../../include/openssl/symhacks.h
+engine_dyn.o: ../../include/openssl/types.h ../../include/openssl/ui.h
+engine_dyn.o: ../cryptlib.h engine_dyn.c engine_int.h
 engine_err.o: ../../include/openssl/asn1.h ../../include/openssl/bio.h
 engine_err.o: ../../include/openssl/bn.h ../../include/openssl/crypto.h
 engine_err.o: ../../include/openssl/dh.h ../../include/openssl/dsa.h
index 0558f20..4c03be3 100644 (file)
@@ -73,6 +73,7 @@
 #include <openssl/rand.h>
 #include <openssl/ui.h>
 #include <openssl/symhacks.h>
+#include <openssl/err.h>
 
 #ifdef  __cplusplus
 extern "C" {
@@ -487,6 +488,78 @@ void ENGINE_load_engine_ciphers(ENGINE *e);
  * doesn't have it */
 const EVP_CIPHER *ENGINE_get_cipher_by_name(ENGINE *e,const char *name);
 
+/**************************/
+/* DYNAMIC ENGINE SUPPORT */
+/**************************/
+
+/* Binary/behaviour compatibility levels */
+#define OSSL_DYNAMIC_VERSION           (unsigned long)0x00010100
+/* Binary versions older than this are too old for us (whether we're a loader or
+ * a loadee) */
+#define OSSL_DYNAMIC_OLDEST            (unsigned long)0x00010100
+
+/* When compiling an ENGINE entirely as an external shared library, loadable by
+ * the "dynamic" ENGINE, these types are needed. The 'dynamic_fns' structure
+ * type provides the calling application's (or library's) error functionality
+ * and memory management function pointers to the loaded library. These should
+ * be used/set in the loaded library code so that the loading application's
+ * 'state' will be used/changed in all operations. */
+typedef void *(*dynamic_MEM_malloc_cb)(size_t);
+typedef void *(*dynamic_MEM_realloc_cb)(void *, size_t);
+typedef void (*dynamic_MEM_free_cb)(void *);
+typedef struct st_dynamic_MEM_fns {
+       dynamic_MEM_malloc_cb                   malloc_cb;
+       dynamic_MEM_realloc_cb                  realloc_cb;
+       dynamic_MEM_free_cb                     free_cb;
+       } dynamic_MEM_fns;
+typedef struct st_dynamic_fns {
+       const ERR_FNS                           *err_fns;
+       const CRYPTO_EX_DATA_IMPL               *ex_data_fns;
+       dynamic_MEM_fns                         mem_fns;
+       } dynamic_fns;
+
+/* The version checking function should be of this prototype. NB: The
+ * ossl_version value passed in is the OSSL_DYNAMIC_VERSION of the loading code.
+ * If this function returns zero, it indicates a (potential) version
+ * incompatibility and the loaded library doesn't believe it can proceed.
+ * Otherwise, the returned value is the (latest) version supported by the
+ * loading library. The loader may still decide that the loaded code's version
+ * is unsatisfactory and could veto the load. The function is expected to
+ * be implemented with the symbol name "v_check", and a default implementation
+ * can be fully instantiated with IMPLEMENT_DYNAMIC_CHECK_FN(). */
+typedef unsigned long (*dynamic_v_check_fn)(unsigned long ossl_version);
+#define IMPLEMENT_DYNAMIC_CHECK_FN() \
+       unsigned long v_check(unsigned long v) { \
+               if(v >= OSSL_DYNAMIC_OLDEST) return OSSL_DYNAMIC_VERSION; \
+               return 0; }
+
+/* This function is passed the ENGINE structure to initialise with its own
+ * function and command settings. It should not adjust the structural or
+ * functional reference counts. If this function returns zero, (a) the load will
+ * be aborted, (b) the previous ENGINE state will be memcpy'd back onto the
+ * structure, and (c) the shared library will be unloaded. So implementations
+ * should do their own internal cleanup in failure circumstances otherwise they
+ * could leak. The 'id' parameter, if non-NULL, represents the ENGINE id that
+ * the loader is looking for. If this is NULL, the shared library can choose to
+ * return failure or to initialise a 'default' ENGINE. If non-NULL, the shared
+ * library must initialise only an ENGINE matching the passed 'id'. The function
+ * is expected to be implemented with the symbol name "bind_engine". A standard
+ * implementation can be instantiated with IMPLEMENT_DYNAMIC_BIND_FN(fn) where
+ * the parameter 'fn' is a callback function that populates the ENGINE structure
+ * and returns an int value (zero for failure). 'fn' should have prototype;
+ *    [static] int fn(ENGINE *e, const char *id); */
+typedef int (*dynamic_bind_engine)(ENGINE *e, const char *id,
+                               const dynamic_fns *fns);
+#define IMPLEMENT_DYNAMIC_BIND_FN(fn) \
+       int bind_engine(ENGINE *e, const char *id, const dynamic_fns *fns) { \
+               if(!CRYPTO_set_mem_functions(fns->mem_fns.malloc_cb, \
+                       fns->mem_fns.realloc_cb, fns->mem_fns.free_cb)) \
+                       return 0; \
+               if(!CRYPTO_set_ex_data_implementation(fns->ex_data_fns)) \
+                       return 0; \
+               if(!ERR_set_implementation(fns->err_fns)) return 0; \
+               if(!fn(e,id)) return 0; \
+               return 1; }
 
 /* Obligatory error function. */
 void ERR_load_ENGINE_strings(void);
@@ -513,6 +586,9 @@ void ERR_load_ENGINE_strings(void);
 #define ENGINE_F_CSWIFT_MOD_EXP                                 102
 #define ENGINE_F_CSWIFT_MOD_EXP_CRT                     103
 #define ENGINE_F_CSWIFT_RSA_MOD_EXP                     104
+#define ENGINE_F_DYNAMIC_CTRL                           180
+#define ENGINE_F_DYNAMIC_GET_DATA_CTX                   181
+#define ENGINE_F_DYNAMIC_LOAD                           182
 #define ENGINE_F_ENGINE_ADD                             105
 #define ENGINE_F_ENGINE_BY_ID                           106
 #define ENGINE_F_ENGINE_CMD_IS_EXECUTABLE               170
@@ -552,6 +628,7 @@ void ERR_load_ENGINE_strings(void);
 #define ENGINE_F_NURON_FINISH                           157
 #define ENGINE_F_NURON_INIT                             156
 #define ENGINE_F_NURON_MOD_EXP                          158
+#define ENGINE_F_SET_DATA_CTX                           183
 #define ENGINE_F_UBSEC_CTRL                             176
 #define ENGINE_F_UBSEC_DSA_SIGN                                 163
 #define ENGINE_F_UBSEC_DSA_VERIFY                       164
@@ -586,6 +663,7 @@ void ERR_load_ENGINE_strings(void);
 #define ENGINE_R_ID_OR_NAME_MISSING                     108
 #define ENGINE_R_INIT_FAILED                            109
 #define ENGINE_R_INTERNAL_LIST_ERROR                    110
+#define ENGINE_R_INVALID_ARGUMENT                       143
 #define ENGINE_R_INVALID_CMD_NAME                       137
 #define ENGINE_R_INVALID_CMD_NUMBER                     138
 #define ENGINE_R_MISSING_KEY_COMPONENTS                         111
@@ -593,6 +671,7 @@ void ERR_load_ENGINE_strings(void);
 #define ENGINE_R_NOT_LOADED                             112
 #define ENGINE_R_NO_CALLBACK                            127
 #define ENGINE_R_NO_CONTROL_FUNCTION                    120
+#define ENGINE_R_NO_INDEX                               144
 #define ENGINE_R_NO_KEY                                         124
 #define ENGINE_R_NO_LOAD_FUNCTION                       125
 #define ENGINE_R_NO_REFERENCE                           130
@@ -605,6 +684,7 @@ void ERR_load_ENGINE_strings(void);
 #define ENGINE_R_RSA_NOT_IMPLEMENTED                    141
 #define ENGINE_R_SIZE_TOO_LARGE_OR_TOO_SMALL            122
 #define ENGINE_R_UNIT_FAILURE                           115
+#define ENGINE_R_VERSION_INCOMPATIBILITY                145
 
 #ifdef  __cplusplus
 }
diff --git a/crypto/engine/engine_dyn.c b/crypto/engine/engine_dyn.c
new file mode 100644 (file)
index 0000000..556e9f3
--- /dev/null
@@ -0,0 +1,419 @@
+/* crypto/engine/engine_dyn.c */
+/* Written by Geoff Thorpe (geoff@geoffthorpe.net) for the OpenSSL
+ * project 2000.
+ */
+/* ====================================================================
+ * Copyright (c) 1999 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+
+
+#include <stdio.h>
+#include <openssl/crypto.h>
+#include "cryptlib.h"
+#include "engine_int.h"
+#include <openssl/engine.h>
+#include <openssl/dso.h>
+
+/* Shared libraries implementing ENGINEs for use by the "dynamic" ENGINE loader
+ * should implement the hook-up functions with the following prototypes. */
+
+/* Our ENGINE handlers */
+static int dynamic_init(ENGINE *e);
+static int dynamic_finish(ENGINE *e);
+static int dynamic_ctrl(ENGINE *e, int cmd, long i, void *p, void (*f)());
+/* Predeclare our context type */
+typedef struct st_dynamic_data_ctx dynamic_data_ctx;
+/* The implementation for the important control command */
+static int dynamic_load(ENGINE *e, dynamic_data_ctx *ctx);
+
+#define DYNAMIC_CMD_SO_PATH            ENGINE_CMD_BASE
+#define DYNAMIC_CMD_NO_VCHECK          (ENGINE_CMD_BASE + 1)
+#define DYNAMIC_CMD_ENGINE_ID          (ENGINE_CMD_BASE + 2)
+#define DYNAMIC_CMD_LIST_ADD           (ENGINE_CMD_BASE + 3)
+#define DYNAMIC_CMD_LOAD               (ENGINE_CMD_BASE + 4)
+
+/* The constants used when creating the ENGINE */
+static const char *engine_dynamic_id = "dynamic";
+static const char *engine_dynamic_name = "Dynamic engine loading support";
+static const ENGINE_CMD_DEFN dynamic_cmd_defns[] = {
+       {DYNAMIC_CMD_SO_PATH,
+               "SO_PATH",
+               "Specifies the path to the new ENGINE shared library",
+               ENGINE_CMD_FLAG_STRING},
+       {DYNAMIC_CMD_NO_VCHECK,
+               "NO_VCHECK",
+               "Specifies to continue even if version checking fails (boolean)",
+               ENGINE_CMD_FLAG_NUMERIC},
+       {DYNAMIC_CMD_ENGINE_ID,
+               "ENGINE_ID",
+               "Specifies an ENGINE id name for loading",
+               ENGINE_CMD_FLAG_STRING},
+       {DYNAMIC_CMD_LIST_ADD,
+               "LIST_ADD",
+               "Whether to add a loaded ENGINE to the internal list (0=no,1=yes,2=mandatory)",
+               ENGINE_CMD_FLAG_NUMERIC},
+       {DYNAMIC_CMD_LOAD,
+               "LOAD",
+               "Load up the ENGINE specified by other settings",
+               ENGINE_CMD_FLAG_NO_INPUT},
+       {0, NULL, NULL, 0}
+       };
+
+/* Loading code stores state inside the ENGINE structure via the "ex_data"
+ * element. We load all our state into a single structure and use that as a
+ * single context in the "ex_data" stack. */
+struct st_dynamic_data_ctx
+       {
+       /* The DSO object we load that supplies the ENGINE code */
+       DSO *dynamic_dso;
+       /* The function pointer to the version checking shared library function */
+       dynamic_v_check_fn v_check;
+       /* The function pointer to the engine-binding shared library function */
+       dynamic_bind_engine bind_engine;
+       /* The default name/path for loading the shared library */
+       const char *DYNAMIC_LIBNAME;
+       /* Whether to continue loading on a version check failure */
+       int no_vcheck;
+       /* If non-NULL, stipulates the 'id' of the ENGINE to be loaded */
+       const char *engine_id;
+       /* If non-zero, a successfully loaded ENGINE should be added to the internal
+        * ENGINE list. If 2, the add must succeed or the entire load should fail. */
+       int list_add_value;
+       /* The symbol name for the version checking function */
+       const char *DYNAMIC_F1;
+       /* The symbol name for the "initialise ENGINE structure" function */
+       const char *DYNAMIC_F2;
+       };
+
+/* This is the "ex_data" index we obtain and reserve for use with our context
+ * structure. */
+static int dynamic_ex_data_idx = -1;
+
+/* Because our ex_data element may or may not get allocated depending on whether
+ * a "first-use" occurs before the ENGINE is freed, we have a memory leak
+ * problem to solve. We can't declare a "new" handler for the ex_data as we
+ * don't want a dynamic_data_ctx in *all* ENGINE structures of all types (this
+ * is a bug in the design of CRYPTO_EX_DATA). As such, we just declare a "free"
+ * handler and that will get called if an ENGINE is being destroyed and there
+ * was an ex_data element corresponding to our context type. */
+static void dynamic_data_ctx_free_func(void *parent, void *ptr,
+                       CRYPTO_EX_DATA *ad, int idx, long argl, void *argp)
+       {
+       if(ptr)
+               {
+               dynamic_data_ctx *ctx = (dynamic_data_ctx *)ptr;
+               if(ctx->dynamic_dso)
+                       DSO_free(ctx->dynamic_dso);
+               OPENSSL_free(ctx);
+               }
+       }
+
+/* Construct the per-ENGINE context. We create it blindly and then use a lock to
+ * check for a race - if so, all but one of the threads "racing" will have
+ * wasted their time. The alternative involves creating everything inside the
+ * lock which is far worse. */
+static int dynamic_set_data_ctx(ENGINE *e, dynamic_data_ctx **ctx)
+       {
+       dynamic_data_ctx *c;
+       c = OPENSSL_malloc(sizeof(dynamic_data_ctx));
+       if(!ctx)
+               {
+               ENGINEerr(ENGINE_F_SET_DATA_CTX,ERR_R_MALLOC_FAILURE);
+               return 0;
+               }
+       memset(c, 0, sizeof(dynamic_data_ctx));
+       c->dynamic_dso = NULL;
+       c->v_check = NULL;
+       c->bind_engine = NULL;
+       c->DYNAMIC_LIBNAME = NULL;
+       c->no_vcheck = 0;
+       c->engine_id = NULL;
+       c->list_add_value = 0;
+       c->DYNAMIC_F1 = "v_check";
+       c->DYNAMIC_F2 = "bind_engine";
+       CRYPTO_w_lock(CRYPTO_LOCK_ENGINE);
+       if((*ctx = (dynamic_data_ctx *)ENGINE_get_ex_data(e,
+                               dynamic_ex_data_idx)) == NULL)
+               {
+               /* Good, we're the first */
+               ENGINE_set_ex_data(e, dynamic_ex_data_idx, c);
+               *ctx = c;
+               c = NULL;
+               }
+       CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE);
+       /* If we lost the race to set the context, c is non-NULL and *ctx is the
+        * context of the thread that won. */
+       if(c)
+               OPENSSL_free(c);
+       return 1;
+       }
+
+/* This function retrieves the context structure from an ENGINE's "ex_data", or
+ * if it doesn't exist yet, sets it up. */
+static dynamic_data_ctx *dynamic_get_data_ctx(ENGINE *e)
+       {
+       dynamic_data_ctx *ctx;
+       if(dynamic_ex_data_idx < 0)
+               {
+               /* Create and register the ENGINE ex_data, and associate our
+                * "free" function with it to ensure any allocated contexts get
+                * freed when an ENGINE goes underground. */
+               int new_idx = ENGINE_get_ex_new_index(0, NULL, NULL, NULL,
+                                       dynamic_data_ctx_free_func);
+               if(new_idx == -1)
+                       {
+                       ENGINEerr(ENGINE_F_DYNAMIC_GET_DATA_CTX,ENGINE_R_NO_INDEX);
+                       return NULL;
+                       }
+               CRYPTO_w_lock(CRYPTO_LOCK_ENGINE);
+               /* Avoid a race by checking again inside this lock */
+               if(dynamic_ex_data_idx < 0)
+                       {
+                       /* Good, someone didn't beat us to it */
+                       dynamic_ex_data_idx = new_idx;
+                       new_idx = -1;
+                       }
+               CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE);
+               /* In theory we could "give back" the index here if
+                * (new_idx>-1), but it's not possible and wouldn't gain us much
+                * if it were. */
+               }
+       ctx = (dynamic_data_ctx *)ENGINE_get_ex_data(e, dynamic_ex_data_idx);
+       /* Check if the context needs to be created */
+       if((ctx == NULL) && !dynamic_set_data_ctx(e, &ctx))
+               /* "set_data" will set errors if necessary */
+               return NULL;
+       return ctx;
+       }
+
+/* As this is only ever called once, there's no need for locking
+ * (indeed - the lock will already be held by our caller!!!) */
+ENGINE *ENGINE_dynamic()
+       {
+       ENGINE *ret = ENGINE_new();
+       if(!ret)
+               return NULL;
+       if(!ENGINE_set_id(ret, engine_dynamic_id) ||
+                       !ENGINE_set_name(ret, engine_dynamic_name) ||
+                       !ENGINE_set_init_function(ret, dynamic_init) ||
+                       !ENGINE_set_finish_function(ret, dynamic_finish) ||
+                       !ENGINE_set_ctrl_function(ret, dynamic_ctrl) ||
+                       !ENGINE_set_flags(ret, ENGINE_FLAGS_BY_ID_COPY) ||
+                       !ENGINE_set_cmd_defns(ret, dynamic_cmd_defns))
+               {
+               ENGINE_free(ret);
+               return NULL;
+               }
+       return ret;
+       }
+
+static int dynamic_init(ENGINE *e)
+       {
+       /* We always return failure - the "dyanamic" engine itself can't be used
+        * for anything. */
+       return 0;
+       }
+
+static int dynamic_finish(ENGINE *e)
+       {
+       /* This should never be called on account of "dynamic_init" always
+        * failing. */
+       return 0;
+       }
+
+static int dynamic_ctrl(ENGINE *e, int cmd, long i, void *p, void (*f)())
+       {
+       dynamic_data_ctx *ctx = dynamic_get_data_ctx(e);
+       int initialised;
+       
+       if(!ctx)
+               {
+               ENGINEerr(ENGINE_F_DYNAMIC_CTRL,ENGINE_R_NOT_LOADED);
+               return 0;
+               }
+       initialised = ((ctx->dynamic_dso == NULL) ? 0 : 1);
+       /* All our control commands require the ENGINE to be uninitialised */
+       if(initialised)
+               {
+               ENGINEerr(ENGINE_F_DYNAMIC_CTRL,
+                       ENGINE_R_ALREADY_LOADED);
+               return 0;
+               }
+       switch(cmd)
+               {
+       case DYNAMIC_CMD_SO_PATH:
+               /* a NULL 'p' or a string of zero-length is the same thing */
+               if(p && (strlen((const char *)p) < 1))
+                       p = NULL;
+               ctx->DYNAMIC_LIBNAME = (const char *)p;
+               return 1;
+       case DYNAMIC_CMD_NO_VCHECK:
+               ctx->no_vcheck = ((i == 0) ? 0 : 1);
+               return 1;
+       case DYNAMIC_CMD_ENGINE_ID:
+               /* a NULL 'p' or a string of zero-length is the same thing */
+               if(p && (strlen((const char *)p) < 1))
+                       p = NULL;
+               ctx->engine_id = (const char *)p;
+               return 1;
+       case DYNAMIC_CMD_LIST_ADD:
+               if((i < 0) || (i > 2))
+                       {
+                       ENGINEerr(ENGINE_F_DYNAMIC_CTRL,
+                               ENGINE_R_INVALID_ARGUMENT);
+                       return 0;
+                       }
+               ctx->list_add_value = (int)i;
+               return 1;
+       case DYNAMIC_CMD_LOAD:
+               return dynamic_load(e, ctx);
+       default:
+               break;
+               }
+       ENGINEerr(ENGINE_F_DYNAMIC_CTRL,ENGINE_R_CTRL_COMMAND_NOT_IMPLEMENTED);
+       return 0;
+       }
+
+static int dynamic_load(ENGINE *e, dynamic_data_ctx *ctx)
+       {
+       ENGINE cpy;
+       dynamic_fns fns;
+
+       if(!ctx->DYNAMIC_LIBNAME || ((ctx->dynamic_dso = DSO_load(NULL,
+                               ctx->DYNAMIC_LIBNAME, NULL, 0)) == NULL))
+               {
+               ENGINEerr(ENGINE_F_DYNAMIC_LOAD,
+                       ENGINE_R_DSO_NOT_FOUND);
+               return 0;
+               }
+       /* We have to find a bind function otherwise it'll always end badly */
+       if(!(ctx->bind_engine = (dynamic_bind_engine)DSO_bind_func(
+                                       ctx->dynamic_dso, ctx->DYNAMIC_F2)))
+               {
+               ctx->bind_engine = NULL;
+               DSO_free(ctx->dynamic_dso);
+               ctx->dynamic_dso = NULL;
+               ENGINEerr(ENGINE_F_DYNAMIC_LOAD,
+                       ENGINE_R_DSO_FAILURE);
+               return 0;
+               }
+       /* Do we perform version checking? */
+       if(!ctx->no_vcheck)
+               {
+               unsigned long vcheck_res = 0;
+               /* Now we try to find a version checking function and decide how
+                * to cope with failure if/when it fails. */
+               ctx->v_check = (dynamic_v_check_fn)DSO_bind_func(
+                               ctx->dynamic_dso, ctx->DYNAMIC_F1);
+               if(ctx->v_check)
+                       vcheck_res = ctx->v_check(OSSL_DYNAMIC_VERSION);
+               /* We fail if the version checker veto'd the load *or* if it is
+                * deferring to us (by returning its version) and we think it is
+                * too old. */
+               if(vcheck_res < OSSL_DYNAMIC_OLDEST)
+                       {
+                       /* Fail */
+                       ctx->bind_engine = NULL;
+                       ctx->v_check = NULL;
+                       DSO_free(ctx->dynamic_dso);
+                       ctx->dynamic_dso = NULL;
+                       ENGINEerr(ENGINE_F_DYNAMIC_LOAD,
+                               ENGINE_R_VERSION_INCOMPATIBILITY);
+                       return 0;
+                       }
+               }
+       /* First binary copy the ENGINE structure so that we can roll back if
+        * the hand-over fails */
+       memcpy(&cpy, e, sizeof(ENGINE));
+       /* Provide the ERR, "ex_data", and memory callbacks so the loaded
+        * library uses our own state (and locking) rather than its own. */
+       fns.err_fns = ERR_get_implementation();
+       fns.ex_data_fns = CRYPTO_get_ex_data_implementation();
+       CRYPTO_get_mem_functions(&fns.mem_fns.malloc_cb,
+                               &fns.mem_fns.realloc_cb,
+                               &fns.mem_fns.free_cb);
+       /* Try to bind the ENGINE onto our own ENGINE structure */
+       if(!ctx->bind_engine(e, ctx->engine_id, &fns))
+               {
+               ctx->bind_engine = NULL;
+               ctx->v_check = NULL;
+               DSO_free(ctx->dynamic_dso);
+               ctx->dynamic_dso = NULL;
+               ENGINEerr(ENGINE_F_DYNAMIC_LOAD,ENGINE_R_INIT_FAILED);
+               /* Copy the original ENGINE structure back */
+               memcpy(e, &cpy, sizeof(ENGINE));
+               return 0;
+               }
+       /* Do we try to add this ENGINE to the internal list too? */
+       if(ctx->list_add_value > 0)
+               {
+               if(!ENGINE_add(e))
+                       {
+                       /* Do we tolerate this or fail? */
+                       if(ctx->list_add_value > 1)
+                               {
+                               /* Fail - NB: By this time, it's too late to
+                                * rollback, and trying to do so allows the
+                                * bind_engine() code to have created leaks. We
+                                * just have to fail where we are, after the
+                                * ENGINE has changed. */
+                               ENGINEerr(ENGINE_F_DYNAMIC_LOAD,
+                                       ENGINE_R_CONFLICTING_ENGINE_ID);
+                               return 0;
+                               }
+                       /* Tolerate */
+                       ERR_clear_error();
+                       }
+               }
+       return 1;
+       }
index 6f21e7c..301415d 100644 (file)
@@ -79,6 +79,9 @@ static ERR_STRING_DATA ENGINE_str_functs[]=
 {ERR_PACK(0,ENGINE_F_CSWIFT_MOD_EXP,0),        "CSWIFT_MOD_EXP"},
 {ERR_PACK(0,ENGINE_F_CSWIFT_MOD_EXP_CRT,0),    "CSWIFT_MOD_EXP_CRT"},
 {ERR_PACK(0,ENGINE_F_CSWIFT_RSA_MOD_EXP,0),    "CSWIFT_RSA_MOD_EXP"},
+{ERR_PACK(0,ENGINE_F_DYNAMIC_CTRL,0),  "DYNAMIC_CTRL"},
+{ERR_PACK(0,ENGINE_F_DYNAMIC_GET_DATA_CTX,0),  "DYNAMIC_GET_DATA_CTX"},
+{ERR_PACK(0,ENGINE_F_DYNAMIC_LOAD,0),  "DYNAMIC_LOAD"},
 {ERR_PACK(0,ENGINE_F_ENGINE_ADD,0),    "ENGINE_add"},
 {ERR_PACK(0,ENGINE_F_ENGINE_BY_ID,0),  "ENGINE_by_id"},
 {ERR_PACK(0,ENGINE_F_ENGINE_CMD_IS_EXECUTABLE,0),      "ENGINE_cmd_is_executable"},
@@ -118,6 +121,7 @@ static ERR_STRING_DATA ENGINE_str_functs[]=
 {ERR_PACK(0,ENGINE_F_NURON_FINISH,0),  "NURON_FINISH"},
 {ERR_PACK(0,ENGINE_F_NURON_INIT,0),    "NURON_INIT"},
 {ERR_PACK(0,ENGINE_F_NURON_MOD_EXP,0), "NURON_MOD_EXP"},
+{ERR_PACK(0,ENGINE_F_SET_DATA_CTX,0),  "SET_DATA_CTX"},
 {ERR_PACK(0,ENGINE_F_UBSEC_CTRL,0),    "UBSEC_CTRL"},
 {ERR_PACK(0,ENGINE_F_UBSEC_DSA_SIGN,0),        "UBSEC_DSA_SIGN"},
 {ERR_PACK(0,ENGINE_F_UBSEC_DSA_VERIFY,0),      "UBSEC_DSA_VERIFY"},
@@ -155,6 +159,7 @@ static ERR_STRING_DATA ENGINE_str_reasons[]=
 {ENGINE_R_ID_OR_NAME_MISSING             ,"'id' or 'name' missing"},
 {ENGINE_R_INIT_FAILED                    ,"init failed"},
 {ENGINE_R_INTERNAL_LIST_ERROR            ,"internal list error"},
+{ENGINE_R_INVALID_ARGUMENT               ,"invalid argument"},
 {ENGINE_R_INVALID_CMD_NAME               ,"invalid cmd name"},
 {ENGINE_R_INVALID_CMD_NUMBER             ,"invalid cmd number"},
 {ENGINE_R_MISSING_KEY_COMPONENTS         ,"missing key components"},
@@ -162,6 +167,7 @@ static ERR_STRING_DATA ENGINE_str_reasons[]=
 {ENGINE_R_NOT_LOADED                     ,"not loaded"},
 {ENGINE_R_NO_CALLBACK                    ,"no callback"},
 {ENGINE_R_NO_CONTROL_FUNCTION            ,"no control function"},
+{ENGINE_R_NO_INDEX                       ,"no index"},
 {ENGINE_R_NO_KEY                         ,"no key"},
 {ENGINE_R_NO_LOAD_FUNCTION               ,"no load function"},
 {ENGINE_R_NO_REFERENCE                   ,"no reference"},
@@ -174,6 +180,7 @@ static ERR_STRING_DATA ENGINE_str_reasons[]=
 {ENGINE_R_RSA_NOT_IMPLEMENTED            ,"rsa not implemented"},
 {ENGINE_R_SIZE_TOO_LARGE_OR_TOO_SMALL    ,"size too large or too small"},
 {ENGINE_R_UNIT_FAILURE                   ,"unit failure"},
+{ENGINE_R_VERSION_INCOMPATIBILITY        ,"version incompatibility"},
 {0,NULL}
        };
 
index 126fef7..b51d191 100644 (file)
@@ -143,6 +143,10 @@ struct engine_st
 /* Returns a structure of software only methods (the default). */
 ENGINE *ENGINE_openssl();
 
+/* Returns the "dynamic" ENGINE for loading entire ENGINE implementations from
+ * shared libraries. */
+ENGINE *ENGINE_dynamic();
+
 #ifndef OPENSSL_NO_HW
 
 #ifndef OPENSSL_NO_HW_CSWIFT
index 926ff5a..ab5652f 100644 (file)
@@ -181,17 +181,20 @@ static int engine_list_remove(ENGINE *e)
 static int engine_internal_check(void)
        {
        int toret = 1;
-       ENGINE *def_engine;
+       ENGINE *def_engine1, *def_engine2;
        if(engine_list_flag)
                return 1;
        /* This is our first time up, we need to populate the list
         * with our statically compiled-in engines. */
-       def_engine = ENGINE_openssl();
-       if(!engine_list_add(def_engine))
+       def_engine1 = ENGINE_openssl();
+       def_engine2 = ENGINE_dynamic();
+       if(!engine_list_add(def_engine1) ||
+                       !engine_list_add(def_engine2))
                toret = 0;
        else
                engine_list_flag = 1;
-       ENGINE_free_util(def_engine, 0);
+       ENGINE_free_util(def_engine1, 0);
+       ENGINE_free_util(def_engine2, 0);
        return 1;
        }