1e1051a39Sopenharmony_ci/*
2e1051a39Sopenharmony_ci * Copyright 2001-2022 The OpenSSL Project Authors. All Rights Reserved.
3e1051a39Sopenharmony_ci *
4e1051a39Sopenharmony_ci * Licensed under the Apache License 2.0 (the "License").  You may not use
5e1051a39Sopenharmony_ci * this file except in compliance with the License.  You can obtain a copy
6e1051a39Sopenharmony_ci * in the file LICENSE in the source distribution or at
7e1051a39Sopenharmony_ci * https://www.openssl.org/source/license.html
8e1051a39Sopenharmony_ci */
9e1051a39Sopenharmony_ci
10e1051a39Sopenharmony_ci/* We need to use some engine deprecated APIs */
11e1051a39Sopenharmony_ci#define OPENSSL_SUPPRESS_DEPRECATED
12e1051a39Sopenharmony_ci
13e1051a39Sopenharmony_ci#include "eng_local.h"
14e1051a39Sopenharmony_ci#include "internal/dso.h"
15e1051a39Sopenharmony_ci#include <openssl/crypto.h>
16e1051a39Sopenharmony_ci
17e1051a39Sopenharmony_ci/*
18e1051a39Sopenharmony_ci * Shared libraries implementing ENGINEs for use by the "dynamic" ENGINE
19e1051a39Sopenharmony_ci * loader should implement the hook-up functions with the following
20e1051a39Sopenharmony_ci * prototypes.
21e1051a39Sopenharmony_ci */
22e1051a39Sopenharmony_ci
23e1051a39Sopenharmony_ci/* Our ENGINE handlers */
24e1051a39Sopenharmony_cistatic int dynamic_init(ENGINE *e);
25e1051a39Sopenharmony_cistatic int dynamic_finish(ENGINE *e);
26e1051a39Sopenharmony_cistatic int dynamic_ctrl(ENGINE *e, int cmd, long i, void *p,
27e1051a39Sopenharmony_ci                        void (*f) (void));
28e1051a39Sopenharmony_ci/* Predeclare our context type */
29e1051a39Sopenharmony_citypedef struct st_dynamic_data_ctx dynamic_data_ctx;
30e1051a39Sopenharmony_ci/* The implementation for the important control command */
31e1051a39Sopenharmony_cistatic int dynamic_load(ENGINE *e, dynamic_data_ctx *ctx);
32e1051a39Sopenharmony_ci
33e1051a39Sopenharmony_ci#define DYNAMIC_CMD_SO_PATH             ENGINE_CMD_BASE
34e1051a39Sopenharmony_ci#define DYNAMIC_CMD_NO_VCHECK           (ENGINE_CMD_BASE + 1)
35e1051a39Sopenharmony_ci#define DYNAMIC_CMD_ID                  (ENGINE_CMD_BASE + 2)
36e1051a39Sopenharmony_ci#define DYNAMIC_CMD_LIST_ADD            (ENGINE_CMD_BASE + 3)
37e1051a39Sopenharmony_ci#define DYNAMIC_CMD_DIR_LOAD            (ENGINE_CMD_BASE + 4)
38e1051a39Sopenharmony_ci#define DYNAMIC_CMD_DIR_ADD             (ENGINE_CMD_BASE + 5)
39e1051a39Sopenharmony_ci#define DYNAMIC_CMD_LOAD                (ENGINE_CMD_BASE + 6)
40e1051a39Sopenharmony_ci
41e1051a39Sopenharmony_ci/* The constants used when creating the ENGINE */
42e1051a39Sopenharmony_cistatic const char *engine_dynamic_id = "dynamic";
43e1051a39Sopenharmony_cistatic const char *engine_dynamic_name = "Dynamic engine loading support";
44e1051a39Sopenharmony_cistatic const ENGINE_CMD_DEFN dynamic_cmd_defns[] = {
45e1051a39Sopenharmony_ci    {DYNAMIC_CMD_SO_PATH,
46e1051a39Sopenharmony_ci     "SO_PATH",
47e1051a39Sopenharmony_ci     "Specifies the path to the new ENGINE shared library",
48e1051a39Sopenharmony_ci     ENGINE_CMD_FLAG_STRING},
49e1051a39Sopenharmony_ci    {DYNAMIC_CMD_NO_VCHECK,
50e1051a39Sopenharmony_ci     "NO_VCHECK",
51e1051a39Sopenharmony_ci     "Specifies to continue even if version checking fails (boolean)",
52e1051a39Sopenharmony_ci     ENGINE_CMD_FLAG_NUMERIC},
53e1051a39Sopenharmony_ci    {DYNAMIC_CMD_ID,
54e1051a39Sopenharmony_ci     "ID",
55e1051a39Sopenharmony_ci     "Specifies an ENGINE id name for loading",
56e1051a39Sopenharmony_ci     ENGINE_CMD_FLAG_STRING},
57e1051a39Sopenharmony_ci    {DYNAMIC_CMD_LIST_ADD,
58e1051a39Sopenharmony_ci     "LIST_ADD",
59e1051a39Sopenharmony_ci     "Whether to add a loaded ENGINE to the internal list (0=no,1=yes,2=mandatory)",
60e1051a39Sopenharmony_ci     ENGINE_CMD_FLAG_NUMERIC},
61e1051a39Sopenharmony_ci    {DYNAMIC_CMD_DIR_LOAD,
62e1051a39Sopenharmony_ci     "DIR_LOAD",
63e1051a39Sopenharmony_ci     "Specifies whether to load from 'DIR_ADD' directories (0=no,1=yes,2=mandatory)",
64e1051a39Sopenharmony_ci     ENGINE_CMD_FLAG_NUMERIC},
65e1051a39Sopenharmony_ci    {DYNAMIC_CMD_DIR_ADD,
66e1051a39Sopenharmony_ci     "DIR_ADD",
67e1051a39Sopenharmony_ci     "Adds a directory from which ENGINEs can be loaded",
68e1051a39Sopenharmony_ci     ENGINE_CMD_FLAG_STRING},
69e1051a39Sopenharmony_ci    {DYNAMIC_CMD_LOAD,
70e1051a39Sopenharmony_ci     "LOAD",
71e1051a39Sopenharmony_ci     "Load up the ENGINE specified by other settings",
72e1051a39Sopenharmony_ci     ENGINE_CMD_FLAG_NO_INPUT},
73e1051a39Sopenharmony_ci    {0, NULL, NULL, 0}
74e1051a39Sopenharmony_ci};
75e1051a39Sopenharmony_ci
76e1051a39Sopenharmony_ci/*
77e1051a39Sopenharmony_ci * Loading code stores state inside the ENGINE structure via the "ex_data"
78e1051a39Sopenharmony_ci * element. We load all our state into a single structure and use that as a
79e1051a39Sopenharmony_ci * single context in the "ex_data" stack.
80e1051a39Sopenharmony_ci */
81e1051a39Sopenharmony_cistruct st_dynamic_data_ctx {
82e1051a39Sopenharmony_ci    /* The DSO object we load that supplies the ENGINE code */
83e1051a39Sopenharmony_ci    DSO *dynamic_dso;
84e1051a39Sopenharmony_ci    /*
85e1051a39Sopenharmony_ci     * The function pointer to the version checking shared library function
86e1051a39Sopenharmony_ci     */
87e1051a39Sopenharmony_ci    dynamic_v_check_fn v_check;
88e1051a39Sopenharmony_ci    /*
89e1051a39Sopenharmony_ci     * The function pointer to the engine-binding shared library function
90e1051a39Sopenharmony_ci     */
91e1051a39Sopenharmony_ci    dynamic_bind_engine bind_engine;
92e1051a39Sopenharmony_ci    /* The default name/path for loading the shared library */
93e1051a39Sopenharmony_ci    char *DYNAMIC_LIBNAME;
94e1051a39Sopenharmony_ci    /* Whether to continue loading on a version check failure */
95e1051a39Sopenharmony_ci    int no_vcheck;
96e1051a39Sopenharmony_ci    /* If non-NULL, stipulates the 'id' of the ENGINE to be loaded */
97e1051a39Sopenharmony_ci    char *engine_id;
98e1051a39Sopenharmony_ci    /*
99e1051a39Sopenharmony_ci     * If non-zero, a successfully loaded ENGINE should be added to the
100e1051a39Sopenharmony_ci     * internal ENGINE list. If 2, the add must succeed or the entire load
101e1051a39Sopenharmony_ci     * should fail.
102e1051a39Sopenharmony_ci     */
103e1051a39Sopenharmony_ci    int list_add_value;
104e1051a39Sopenharmony_ci    /* The symbol name for the version checking function */
105e1051a39Sopenharmony_ci    const char *DYNAMIC_F1;
106e1051a39Sopenharmony_ci    /* The symbol name for the "initialise ENGINE structure" function */
107e1051a39Sopenharmony_ci    const char *DYNAMIC_F2;
108e1051a39Sopenharmony_ci    /*
109e1051a39Sopenharmony_ci     * Whether to never use 'dirs', use 'dirs' as a fallback, or only use
110e1051a39Sopenharmony_ci     * 'dirs' for loading. Default is to use 'dirs' as a fallback.
111e1051a39Sopenharmony_ci     */
112e1051a39Sopenharmony_ci    int dir_load;
113e1051a39Sopenharmony_ci    /* A stack of directories from which ENGINEs could be loaded */
114e1051a39Sopenharmony_ci    STACK_OF(OPENSSL_STRING) *dirs;
115e1051a39Sopenharmony_ci};
116e1051a39Sopenharmony_ci
117e1051a39Sopenharmony_ci/*
118e1051a39Sopenharmony_ci * This is the "ex_data" index we obtain and reserve for use with our context
119e1051a39Sopenharmony_ci * structure.
120e1051a39Sopenharmony_ci */
121e1051a39Sopenharmony_cistatic int dynamic_ex_data_idx = -1;
122e1051a39Sopenharmony_ci
123e1051a39Sopenharmony_cistatic void int_free_str(char *s)
124e1051a39Sopenharmony_ci{
125e1051a39Sopenharmony_ci    OPENSSL_free(s);
126e1051a39Sopenharmony_ci}
127e1051a39Sopenharmony_ci
128e1051a39Sopenharmony_ci/*
129e1051a39Sopenharmony_ci * Because our ex_data element may or may not get allocated depending on
130e1051a39Sopenharmony_ci * whether a "first-use" occurs before the ENGINE is freed, we have a memory
131e1051a39Sopenharmony_ci * leak problem to solve. We can't declare a "new" handler for the ex_data as
132e1051a39Sopenharmony_ci * we don't want a dynamic_data_ctx in *all* ENGINE structures of all types
133e1051a39Sopenharmony_ci * (this is a bug in the design of CRYPTO_EX_DATA). As such, we just declare
134e1051a39Sopenharmony_ci * a "free" handler and that will get called if an ENGINE is being destroyed
135e1051a39Sopenharmony_ci * and there was an ex_data element corresponding to our context type.
136e1051a39Sopenharmony_ci */
137e1051a39Sopenharmony_cistatic void dynamic_data_ctx_free_func(void *parent, void *ptr,
138e1051a39Sopenharmony_ci                                       CRYPTO_EX_DATA *ad, int idx, long argl,
139e1051a39Sopenharmony_ci                                       void *argp)
140e1051a39Sopenharmony_ci{
141e1051a39Sopenharmony_ci    if (ptr) {
142e1051a39Sopenharmony_ci        dynamic_data_ctx *ctx = (dynamic_data_ctx *)ptr;
143e1051a39Sopenharmony_ci        DSO_free(ctx->dynamic_dso);
144e1051a39Sopenharmony_ci        OPENSSL_free(ctx->DYNAMIC_LIBNAME);
145e1051a39Sopenharmony_ci        OPENSSL_free(ctx->engine_id);
146e1051a39Sopenharmony_ci        sk_OPENSSL_STRING_pop_free(ctx->dirs, int_free_str);
147e1051a39Sopenharmony_ci        OPENSSL_free(ctx);
148e1051a39Sopenharmony_ci    }
149e1051a39Sopenharmony_ci}
150e1051a39Sopenharmony_ci
151e1051a39Sopenharmony_ci/*
152e1051a39Sopenharmony_ci * Construct the per-ENGINE context. We create it blindly and then use a lock
153e1051a39Sopenharmony_ci * to check for a race - if so, all but one of the threads "racing" will have
154e1051a39Sopenharmony_ci * wasted their time. The alternative involves creating everything inside the
155e1051a39Sopenharmony_ci * lock which is far worse.
156e1051a39Sopenharmony_ci */
157e1051a39Sopenharmony_cistatic int dynamic_set_data_ctx(ENGINE *e, dynamic_data_ctx **ctx)
158e1051a39Sopenharmony_ci{
159e1051a39Sopenharmony_ci    dynamic_data_ctx *c = OPENSSL_zalloc(sizeof(*c));
160e1051a39Sopenharmony_ci    int ret = 0;
161e1051a39Sopenharmony_ci
162e1051a39Sopenharmony_ci    if (c == NULL) {
163e1051a39Sopenharmony_ci        ERR_raise(ERR_LIB_ENGINE, ERR_R_MALLOC_FAILURE);
164e1051a39Sopenharmony_ci        return 0;
165e1051a39Sopenharmony_ci    }
166e1051a39Sopenharmony_ci    c->dirs = sk_OPENSSL_STRING_new_null();
167e1051a39Sopenharmony_ci    if (c->dirs == NULL) {
168e1051a39Sopenharmony_ci        ERR_raise(ERR_LIB_ENGINE, ERR_R_MALLOC_FAILURE);
169e1051a39Sopenharmony_ci        goto end;
170e1051a39Sopenharmony_ci    }
171e1051a39Sopenharmony_ci    c->DYNAMIC_F1 = "v_check";
172e1051a39Sopenharmony_ci    c->DYNAMIC_F2 = "bind_engine";
173e1051a39Sopenharmony_ci    c->dir_load = 1;
174e1051a39Sopenharmony_ci    if (!CRYPTO_THREAD_write_lock(global_engine_lock))
175e1051a39Sopenharmony_ci        goto end;
176e1051a39Sopenharmony_ci    if ((*ctx = (dynamic_data_ctx *)ENGINE_get_ex_data(e,
177e1051a39Sopenharmony_ci                                                       dynamic_ex_data_idx))
178e1051a39Sopenharmony_ci        == NULL) {
179e1051a39Sopenharmony_ci        /* Good, we're the first */
180e1051a39Sopenharmony_ci        ret = ENGINE_set_ex_data(e, dynamic_ex_data_idx, c);
181e1051a39Sopenharmony_ci        if (ret) {
182e1051a39Sopenharmony_ci            *ctx = c;
183e1051a39Sopenharmony_ci            c = NULL;
184e1051a39Sopenharmony_ci        }
185e1051a39Sopenharmony_ci    }
186e1051a39Sopenharmony_ci    CRYPTO_THREAD_unlock(global_engine_lock);
187e1051a39Sopenharmony_ci    ret = 1;
188e1051a39Sopenharmony_ci    /*
189e1051a39Sopenharmony_ci     * If we lost the race to set the context, c is non-NULL and *ctx is the
190e1051a39Sopenharmony_ci     * context of the thread that won.
191e1051a39Sopenharmony_ci     */
192e1051a39Sopenharmony_ciend:
193e1051a39Sopenharmony_ci    if (c != NULL)
194e1051a39Sopenharmony_ci        sk_OPENSSL_STRING_free(c->dirs);
195e1051a39Sopenharmony_ci    OPENSSL_free(c);
196e1051a39Sopenharmony_ci    return ret;
197e1051a39Sopenharmony_ci}
198e1051a39Sopenharmony_ci
199e1051a39Sopenharmony_ci/*
200e1051a39Sopenharmony_ci * This function retrieves the context structure from an ENGINE's "ex_data",
201e1051a39Sopenharmony_ci * or if it doesn't exist yet, sets it up.
202e1051a39Sopenharmony_ci */
203e1051a39Sopenharmony_cistatic dynamic_data_ctx *dynamic_get_data_ctx(ENGINE *e)
204e1051a39Sopenharmony_ci{
205e1051a39Sopenharmony_ci    dynamic_data_ctx *ctx;
206e1051a39Sopenharmony_ci    if (dynamic_ex_data_idx < 0) {
207e1051a39Sopenharmony_ci        /*
208e1051a39Sopenharmony_ci         * Create and register the ENGINE ex_data, and associate our "free"
209e1051a39Sopenharmony_ci         * function with it to ensure any allocated contexts get freed when
210e1051a39Sopenharmony_ci         * an ENGINE goes underground.
211e1051a39Sopenharmony_ci         */
212e1051a39Sopenharmony_ci        int new_idx = ENGINE_get_ex_new_index(0, NULL, NULL, NULL,
213e1051a39Sopenharmony_ci                                              dynamic_data_ctx_free_func);
214e1051a39Sopenharmony_ci        if (new_idx == -1) {
215e1051a39Sopenharmony_ci            ERR_raise(ERR_LIB_ENGINE, ENGINE_R_NO_INDEX);
216e1051a39Sopenharmony_ci            return NULL;
217e1051a39Sopenharmony_ci        }
218e1051a39Sopenharmony_ci        if (!CRYPTO_THREAD_write_lock(global_engine_lock))
219e1051a39Sopenharmony_ci            return NULL;
220e1051a39Sopenharmony_ci        /* Avoid a race by checking again inside this lock */
221e1051a39Sopenharmony_ci        if (dynamic_ex_data_idx < 0) {
222e1051a39Sopenharmony_ci            /* Good, someone didn't beat us to it */
223e1051a39Sopenharmony_ci            dynamic_ex_data_idx = new_idx;
224e1051a39Sopenharmony_ci            new_idx = -1;
225e1051a39Sopenharmony_ci        }
226e1051a39Sopenharmony_ci        CRYPTO_THREAD_unlock(global_engine_lock);
227e1051a39Sopenharmony_ci        /*
228e1051a39Sopenharmony_ci         * In theory we could "give back" the index here if (new_idx>-1), but
229e1051a39Sopenharmony_ci         * it's not possible and wouldn't gain us much if it were.
230e1051a39Sopenharmony_ci         */
231e1051a39Sopenharmony_ci    }
232e1051a39Sopenharmony_ci    ctx = (dynamic_data_ctx *)ENGINE_get_ex_data(e, dynamic_ex_data_idx);
233e1051a39Sopenharmony_ci    /* Check if the context needs to be created */
234e1051a39Sopenharmony_ci    if ((ctx == NULL) && !dynamic_set_data_ctx(e, &ctx))
235e1051a39Sopenharmony_ci        /* "set_data" will set errors if necessary */
236e1051a39Sopenharmony_ci        return NULL;
237e1051a39Sopenharmony_ci    return ctx;
238e1051a39Sopenharmony_ci}
239e1051a39Sopenharmony_ci
240e1051a39Sopenharmony_cistatic ENGINE *engine_dynamic(void)
241e1051a39Sopenharmony_ci{
242e1051a39Sopenharmony_ci    ENGINE *ret = ENGINE_new();
243e1051a39Sopenharmony_ci    if (ret == NULL)
244e1051a39Sopenharmony_ci        return NULL;
245e1051a39Sopenharmony_ci    if (!ENGINE_set_id(ret, engine_dynamic_id) ||
246e1051a39Sopenharmony_ci        !ENGINE_set_name(ret, engine_dynamic_name) ||
247e1051a39Sopenharmony_ci        !ENGINE_set_init_function(ret, dynamic_init) ||
248e1051a39Sopenharmony_ci        !ENGINE_set_finish_function(ret, dynamic_finish) ||
249e1051a39Sopenharmony_ci        !ENGINE_set_ctrl_function(ret, dynamic_ctrl) ||
250e1051a39Sopenharmony_ci        !ENGINE_set_flags(ret, ENGINE_FLAGS_BY_ID_COPY) ||
251e1051a39Sopenharmony_ci        !ENGINE_set_cmd_defns(ret, dynamic_cmd_defns)) {
252e1051a39Sopenharmony_ci        ENGINE_free(ret);
253e1051a39Sopenharmony_ci        return NULL;
254e1051a39Sopenharmony_ci    }
255e1051a39Sopenharmony_ci    return ret;
256e1051a39Sopenharmony_ci}
257e1051a39Sopenharmony_ci
258e1051a39Sopenharmony_civoid engine_load_dynamic_int(void)
259e1051a39Sopenharmony_ci{
260e1051a39Sopenharmony_ci    ENGINE *toadd = engine_dynamic();
261e1051a39Sopenharmony_ci    if (!toadd)
262e1051a39Sopenharmony_ci        return;
263e1051a39Sopenharmony_ci
264e1051a39Sopenharmony_ci    ERR_set_mark();
265e1051a39Sopenharmony_ci    ENGINE_add(toadd);
266e1051a39Sopenharmony_ci    /*
267e1051a39Sopenharmony_ci     * If the "add" worked, it gets a structural reference. So either way, we
268e1051a39Sopenharmony_ci     * release our just-created reference.
269e1051a39Sopenharmony_ci     */
270e1051a39Sopenharmony_ci    ENGINE_free(toadd);
271e1051a39Sopenharmony_ci    /*
272e1051a39Sopenharmony_ci     * If the "add" didn't work, it was probably a conflict because it was
273e1051a39Sopenharmony_ci     * already added (eg. someone calling ENGINE_load_blah then calling
274e1051a39Sopenharmony_ci     * ENGINE_load_builtin_engines() perhaps).
275e1051a39Sopenharmony_ci     */
276e1051a39Sopenharmony_ci    ERR_pop_to_mark();
277e1051a39Sopenharmony_ci}
278e1051a39Sopenharmony_ci
279e1051a39Sopenharmony_cistatic int dynamic_init(ENGINE *e)
280e1051a39Sopenharmony_ci{
281e1051a39Sopenharmony_ci    /*
282e1051a39Sopenharmony_ci     * We always return failure - the "dynamic" engine itself can't be used
283e1051a39Sopenharmony_ci     * for anything.
284e1051a39Sopenharmony_ci     */
285e1051a39Sopenharmony_ci    return 0;
286e1051a39Sopenharmony_ci}
287e1051a39Sopenharmony_ci
288e1051a39Sopenharmony_cistatic int dynamic_finish(ENGINE *e)
289e1051a39Sopenharmony_ci{
290e1051a39Sopenharmony_ci    /*
291e1051a39Sopenharmony_ci     * This should never be called on account of "dynamic_init" always
292e1051a39Sopenharmony_ci     * failing.
293e1051a39Sopenharmony_ci     */
294e1051a39Sopenharmony_ci    return 0;
295e1051a39Sopenharmony_ci}
296e1051a39Sopenharmony_ci
297e1051a39Sopenharmony_cistatic int dynamic_ctrl(ENGINE *e, int cmd, long i, void *p, void (*f) (void))
298e1051a39Sopenharmony_ci{
299e1051a39Sopenharmony_ci    dynamic_data_ctx *ctx = dynamic_get_data_ctx(e);
300e1051a39Sopenharmony_ci    int initialised;
301e1051a39Sopenharmony_ci
302e1051a39Sopenharmony_ci    if (!ctx) {
303e1051a39Sopenharmony_ci        ERR_raise(ERR_LIB_ENGINE, ENGINE_R_NOT_LOADED);
304e1051a39Sopenharmony_ci        return 0;
305e1051a39Sopenharmony_ci    }
306e1051a39Sopenharmony_ci    initialised = ((ctx->dynamic_dso == NULL) ? 0 : 1);
307e1051a39Sopenharmony_ci    /* All our control commands require the ENGINE to be uninitialised */
308e1051a39Sopenharmony_ci    if (initialised) {
309e1051a39Sopenharmony_ci        ERR_raise(ERR_LIB_ENGINE, ENGINE_R_ALREADY_LOADED);
310e1051a39Sopenharmony_ci        return 0;
311e1051a39Sopenharmony_ci    }
312e1051a39Sopenharmony_ci    switch (cmd) {
313e1051a39Sopenharmony_ci    case DYNAMIC_CMD_SO_PATH:
314e1051a39Sopenharmony_ci        /* a NULL 'p' or a string of zero-length is the same thing */
315e1051a39Sopenharmony_ci        if (p && (strlen((const char *)p) < 1))
316e1051a39Sopenharmony_ci            p = NULL;
317e1051a39Sopenharmony_ci        OPENSSL_free(ctx->DYNAMIC_LIBNAME);
318e1051a39Sopenharmony_ci        if (p)
319e1051a39Sopenharmony_ci            ctx->DYNAMIC_LIBNAME = OPENSSL_strdup(p);
320e1051a39Sopenharmony_ci        else
321e1051a39Sopenharmony_ci            ctx->DYNAMIC_LIBNAME = NULL;
322e1051a39Sopenharmony_ci        return (ctx->DYNAMIC_LIBNAME ? 1 : 0);
323e1051a39Sopenharmony_ci    case DYNAMIC_CMD_NO_VCHECK:
324e1051a39Sopenharmony_ci        ctx->no_vcheck = ((i == 0) ? 0 : 1);
325e1051a39Sopenharmony_ci        return 1;
326e1051a39Sopenharmony_ci    case DYNAMIC_CMD_ID:
327e1051a39Sopenharmony_ci        /* a NULL 'p' or a string of zero-length is the same thing */
328e1051a39Sopenharmony_ci        if (p && (strlen((const char *)p) < 1))
329e1051a39Sopenharmony_ci            p = NULL;
330e1051a39Sopenharmony_ci        OPENSSL_free(ctx->engine_id);
331e1051a39Sopenharmony_ci        if (p)
332e1051a39Sopenharmony_ci            ctx->engine_id = OPENSSL_strdup(p);
333e1051a39Sopenharmony_ci        else
334e1051a39Sopenharmony_ci            ctx->engine_id = NULL;
335e1051a39Sopenharmony_ci        return (ctx->engine_id ? 1 : 0);
336e1051a39Sopenharmony_ci    case DYNAMIC_CMD_LIST_ADD:
337e1051a39Sopenharmony_ci        if ((i < 0) || (i > 2)) {
338e1051a39Sopenharmony_ci            ERR_raise(ERR_LIB_ENGINE, ENGINE_R_INVALID_ARGUMENT);
339e1051a39Sopenharmony_ci            return 0;
340e1051a39Sopenharmony_ci        }
341e1051a39Sopenharmony_ci        ctx->list_add_value = (int)i;
342e1051a39Sopenharmony_ci        return 1;
343e1051a39Sopenharmony_ci    case DYNAMIC_CMD_LOAD:
344e1051a39Sopenharmony_ci        return dynamic_load(e, ctx);
345e1051a39Sopenharmony_ci    case DYNAMIC_CMD_DIR_LOAD:
346e1051a39Sopenharmony_ci        if ((i < 0) || (i > 2)) {
347e1051a39Sopenharmony_ci            ERR_raise(ERR_LIB_ENGINE, ENGINE_R_INVALID_ARGUMENT);
348e1051a39Sopenharmony_ci            return 0;
349e1051a39Sopenharmony_ci        }
350e1051a39Sopenharmony_ci        ctx->dir_load = (int)i;
351e1051a39Sopenharmony_ci        return 1;
352e1051a39Sopenharmony_ci    case DYNAMIC_CMD_DIR_ADD:
353e1051a39Sopenharmony_ci        /* a NULL 'p' or a string of zero-length is the same thing */
354e1051a39Sopenharmony_ci        if (p == NULL || (strlen((const char *)p) < 1)) {
355e1051a39Sopenharmony_ci            ERR_raise(ERR_LIB_ENGINE, ENGINE_R_INVALID_ARGUMENT);
356e1051a39Sopenharmony_ci            return 0;
357e1051a39Sopenharmony_ci        }
358e1051a39Sopenharmony_ci        {
359e1051a39Sopenharmony_ci            char *tmp_str = OPENSSL_strdup(p);
360e1051a39Sopenharmony_ci            if (tmp_str == NULL) {
361e1051a39Sopenharmony_ci                ERR_raise(ERR_LIB_ENGINE, ERR_R_MALLOC_FAILURE);
362e1051a39Sopenharmony_ci                return 0;
363e1051a39Sopenharmony_ci            }
364e1051a39Sopenharmony_ci            if (!sk_OPENSSL_STRING_push(ctx->dirs, tmp_str)) {
365e1051a39Sopenharmony_ci                OPENSSL_free(tmp_str);
366e1051a39Sopenharmony_ci                ERR_raise(ERR_LIB_ENGINE, ERR_R_MALLOC_FAILURE);
367e1051a39Sopenharmony_ci                return 0;
368e1051a39Sopenharmony_ci            }
369e1051a39Sopenharmony_ci        }
370e1051a39Sopenharmony_ci        return 1;
371e1051a39Sopenharmony_ci    default:
372e1051a39Sopenharmony_ci        break;
373e1051a39Sopenharmony_ci    }
374e1051a39Sopenharmony_ci    ERR_raise(ERR_LIB_ENGINE, ENGINE_R_CTRL_COMMAND_NOT_IMPLEMENTED);
375e1051a39Sopenharmony_ci    return 0;
376e1051a39Sopenharmony_ci}
377e1051a39Sopenharmony_ci
378e1051a39Sopenharmony_cistatic int int_load(dynamic_data_ctx *ctx)
379e1051a39Sopenharmony_ci{
380e1051a39Sopenharmony_ci    int num, loop;
381e1051a39Sopenharmony_ci    /* Unless told not to, try a direct load */
382e1051a39Sopenharmony_ci    if ((ctx->dir_load != 2) && (DSO_load(ctx->dynamic_dso,
383e1051a39Sopenharmony_ci                                          ctx->DYNAMIC_LIBNAME, NULL,
384e1051a39Sopenharmony_ci                                          0)) != NULL)
385e1051a39Sopenharmony_ci        return 1;
386e1051a39Sopenharmony_ci    /* If we're not allowed to use 'dirs' or we have none, fail */
387e1051a39Sopenharmony_ci    if (!ctx->dir_load || (num = sk_OPENSSL_STRING_num(ctx->dirs)) < 1)
388e1051a39Sopenharmony_ci        return 0;
389e1051a39Sopenharmony_ci    for (loop = 0; loop < num; loop++) {
390e1051a39Sopenharmony_ci        const char *s = sk_OPENSSL_STRING_value(ctx->dirs, loop);
391e1051a39Sopenharmony_ci        char *merge = DSO_merge(ctx->dynamic_dso, ctx->DYNAMIC_LIBNAME, s);
392e1051a39Sopenharmony_ci        if (!merge)
393e1051a39Sopenharmony_ci            return 0;
394e1051a39Sopenharmony_ci        if (DSO_load(ctx->dynamic_dso, merge, NULL, 0)) {
395e1051a39Sopenharmony_ci            /* Found what we're looking for */
396e1051a39Sopenharmony_ci            OPENSSL_free(merge);
397e1051a39Sopenharmony_ci            return 1;
398e1051a39Sopenharmony_ci        }
399e1051a39Sopenharmony_ci        OPENSSL_free(merge);
400e1051a39Sopenharmony_ci    }
401e1051a39Sopenharmony_ci    return 0;
402e1051a39Sopenharmony_ci}
403e1051a39Sopenharmony_ci
404e1051a39Sopenharmony_ci/*
405e1051a39Sopenharmony_ci * Unfortunately the version checker does not distinguish between
406e1051a39Sopenharmony_ci * engines built for openssl 1.1.x and openssl 3.x, but loading
407e1051a39Sopenharmony_ci * an engine that is built for openssl 1.1.x will cause a fatal
408e1051a39Sopenharmony_ci * error.  Detect such engines, since EVP_PKEY_base_id is exported
409e1051a39Sopenharmony_ci * as a function in openssl 1.1.x, while it is named EVP_PKEY_get_base_id
410e1051a39Sopenharmony_ci * in openssl 3.x.  Therefore we take the presence of that symbol
411e1051a39Sopenharmony_ci * as an indication that the engine will be incompatible.
412e1051a39Sopenharmony_ci */
413e1051a39Sopenharmony_cistatic int using_libcrypto_11(dynamic_data_ctx *ctx)
414e1051a39Sopenharmony_ci{
415e1051a39Sopenharmony_ci    int ret;
416e1051a39Sopenharmony_ci
417e1051a39Sopenharmony_ci    ERR_set_mark();
418e1051a39Sopenharmony_ci    ret = DSO_bind_func(ctx->dynamic_dso, "EVP_PKEY_base_id") != NULL;
419e1051a39Sopenharmony_ci    ERR_pop_to_mark();
420e1051a39Sopenharmony_ci
421e1051a39Sopenharmony_ci    return ret;
422e1051a39Sopenharmony_ci}
423e1051a39Sopenharmony_ci
424e1051a39Sopenharmony_cistatic int dynamic_load(ENGINE *e, dynamic_data_ctx *ctx)
425e1051a39Sopenharmony_ci{
426e1051a39Sopenharmony_ci    ENGINE cpy;
427e1051a39Sopenharmony_ci    dynamic_fns fns;
428e1051a39Sopenharmony_ci
429e1051a39Sopenharmony_ci    if (ctx->dynamic_dso == NULL)
430e1051a39Sopenharmony_ci        ctx->dynamic_dso = DSO_new();
431e1051a39Sopenharmony_ci    if (ctx->dynamic_dso == NULL)
432e1051a39Sopenharmony_ci        return 0;
433e1051a39Sopenharmony_ci    if (!ctx->DYNAMIC_LIBNAME) {
434e1051a39Sopenharmony_ci        if (!ctx->engine_id)
435e1051a39Sopenharmony_ci            return 0;
436e1051a39Sopenharmony_ci        DSO_ctrl(ctx->dynamic_dso, DSO_CTRL_SET_FLAGS,
437e1051a39Sopenharmony_ci                 DSO_FLAG_NAME_TRANSLATION_EXT_ONLY, NULL);
438e1051a39Sopenharmony_ci        ctx->DYNAMIC_LIBNAME =
439e1051a39Sopenharmony_ci            DSO_convert_filename(ctx->dynamic_dso, ctx->engine_id);
440e1051a39Sopenharmony_ci    }
441e1051a39Sopenharmony_ci    if (!int_load(ctx)) {
442e1051a39Sopenharmony_ci        ERR_raise(ERR_LIB_ENGINE, ENGINE_R_DSO_NOT_FOUND);
443e1051a39Sopenharmony_ci        DSO_free(ctx->dynamic_dso);
444e1051a39Sopenharmony_ci        ctx->dynamic_dso = NULL;
445e1051a39Sopenharmony_ci        return 0;
446e1051a39Sopenharmony_ci    }
447e1051a39Sopenharmony_ci    /* We have to find a bind function otherwise it'll always end badly */
448e1051a39Sopenharmony_ci    if (!
449e1051a39Sopenharmony_ci        (ctx->bind_engine =
450e1051a39Sopenharmony_ci         (dynamic_bind_engine) DSO_bind_func(ctx->dynamic_dso,
451e1051a39Sopenharmony_ci                                             ctx->DYNAMIC_F2))) {
452e1051a39Sopenharmony_ci        ctx->bind_engine = NULL;
453e1051a39Sopenharmony_ci        DSO_free(ctx->dynamic_dso);
454e1051a39Sopenharmony_ci        ctx->dynamic_dso = NULL;
455e1051a39Sopenharmony_ci        ERR_raise(ERR_LIB_ENGINE, ENGINE_R_DSO_FAILURE);
456e1051a39Sopenharmony_ci        return 0;
457e1051a39Sopenharmony_ci    }
458e1051a39Sopenharmony_ci    /* Do we perform version checking? */
459e1051a39Sopenharmony_ci    if (!ctx->no_vcheck) {
460e1051a39Sopenharmony_ci        unsigned long vcheck_res = 0;
461e1051a39Sopenharmony_ci        /*
462e1051a39Sopenharmony_ci         * Now we try to find a version checking function and decide how to
463e1051a39Sopenharmony_ci         * cope with failure if/when it fails.
464e1051a39Sopenharmony_ci         */
465e1051a39Sopenharmony_ci        ctx->v_check =
466e1051a39Sopenharmony_ci            (dynamic_v_check_fn) DSO_bind_func(ctx->dynamic_dso,
467e1051a39Sopenharmony_ci                                               ctx->DYNAMIC_F1);
468e1051a39Sopenharmony_ci        if (ctx->v_check)
469e1051a39Sopenharmony_ci            vcheck_res = ctx->v_check(OSSL_DYNAMIC_VERSION);
470e1051a39Sopenharmony_ci        /*
471e1051a39Sopenharmony_ci         * We fail if the version checker veto'd the load *or* if it is
472e1051a39Sopenharmony_ci         * deferring to us (by returning its version) and we think it is too
473e1051a39Sopenharmony_ci         * old. Also fail if this is engine for openssl 1.1.x.
474e1051a39Sopenharmony_ci         */
475e1051a39Sopenharmony_ci        if (vcheck_res < OSSL_DYNAMIC_OLDEST || using_libcrypto_11(ctx)) {
476e1051a39Sopenharmony_ci            /* Fail */
477e1051a39Sopenharmony_ci            ctx->bind_engine = NULL;
478e1051a39Sopenharmony_ci            ctx->v_check = NULL;
479e1051a39Sopenharmony_ci            DSO_free(ctx->dynamic_dso);
480e1051a39Sopenharmony_ci            ctx->dynamic_dso = NULL;
481e1051a39Sopenharmony_ci            ERR_raise(ERR_LIB_ENGINE, ENGINE_R_VERSION_INCOMPATIBILITY);
482e1051a39Sopenharmony_ci            return 0;
483e1051a39Sopenharmony_ci        }
484e1051a39Sopenharmony_ci    }
485e1051a39Sopenharmony_ci    /*
486e1051a39Sopenharmony_ci     * First binary copy the ENGINE structure so that we can roll back if the
487e1051a39Sopenharmony_ci     * hand-over fails
488e1051a39Sopenharmony_ci     */
489e1051a39Sopenharmony_ci    memcpy(&cpy, e, sizeof(ENGINE));
490e1051a39Sopenharmony_ci    /*
491e1051a39Sopenharmony_ci     * Provide the ERR, "ex_data", memory, and locking callbacks so the
492e1051a39Sopenharmony_ci     * loaded library uses our state rather than its own. FIXME: As noted in
493e1051a39Sopenharmony_ci     * engine.h, much of this would be simplified if each area of code
494e1051a39Sopenharmony_ci     * provided its own "summary" structure of all related callbacks. It
495e1051a39Sopenharmony_ci     * would also increase opaqueness.
496e1051a39Sopenharmony_ci     */
497e1051a39Sopenharmony_ci    fns.static_state = ENGINE_get_static_state();
498e1051a39Sopenharmony_ci    CRYPTO_get_mem_functions(&fns.mem_fns.malloc_fn, &fns.mem_fns.realloc_fn,
499e1051a39Sopenharmony_ci                             &fns.mem_fns.free_fn);
500e1051a39Sopenharmony_ci    /*
501e1051a39Sopenharmony_ci     * Now that we've loaded the dynamic engine, make sure no "dynamic"
502e1051a39Sopenharmony_ci     * ENGINE elements will show through.
503e1051a39Sopenharmony_ci     */
504e1051a39Sopenharmony_ci    engine_set_all_null(e);
505e1051a39Sopenharmony_ci
506e1051a39Sopenharmony_ci    /* Try to bind the ENGINE onto our own ENGINE structure */
507e1051a39Sopenharmony_ci    if (!engine_add_dynamic_id(e, (ENGINE_DYNAMIC_ID)ctx->bind_engine, 1)
508e1051a39Sopenharmony_ci            || !ctx->bind_engine(e, ctx->engine_id, &fns)) {
509e1051a39Sopenharmony_ci        engine_remove_dynamic_id(e, 1);
510e1051a39Sopenharmony_ci        ctx->bind_engine = NULL;
511e1051a39Sopenharmony_ci        ctx->v_check = NULL;
512e1051a39Sopenharmony_ci        DSO_free(ctx->dynamic_dso);
513e1051a39Sopenharmony_ci        ctx->dynamic_dso = NULL;
514e1051a39Sopenharmony_ci        ERR_raise(ERR_LIB_ENGINE, ENGINE_R_INIT_FAILED);
515e1051a39Sopenharmony_ci        /* Copy the original ENGINE structure back */
516e1051a39Sopenharmony_ci        memcpy(e, &cpy, sizeof(ENGINE));
517e1051a39Sopenharmony_ci        return 0;
518e1051a39Sopenharmony_ci    }
519e1051a39Sopenharmony_ci    /* Do we try to add this ENGINE to the internal list too? */
520e1051a39Sopenharmony_ci    if (ctx->list_add_value > 0) {
521e1051a39Sopenharmony_ci        if (!ENGINE_add(e)) {
522e1051a39Sopenharmony_ci            /* Do we tolerate this or fail? */
523e1051a39Sopenharmony_ci            if (ctx->list_add_value > 1) {
524e1051a39Sopenharmony_ci                /*
525e1051a39Sopenharmony_ci                 * Fail - NB: By this time, it's too late to rollback, and
526e1051a39Sopenharmony_ci                 * trying to do so allows the bind_engine() code to have
527e1051a39Sopenharmony_ci                 * created leaks. We just have to fail where we are, after
528e1051a39Sopenharmony_ci                 * the ENGINE has changed.
529e1051a39Sopenharmony_ci                 */
530e1051a39Sopenharmony_ci                ERR_raise(ERR_LIB_ENGINE, ENGINE_R_CONFLICTING_ENGINE_ID);
531e1051a39Sopenharmony_ci                return 0;
532e1051a39Sopenharmony_ci            }
533e1051a39Sopenharmony_ci            /* Tolerate */
534e1051a39Sopenharmony_ci            ERR_clear_error();
535e1051a39Sopenharmony_ci        }
536e1051a39Sopenharmony_ci    }
537e1051a39Sopenharmony_ci    return 1;
538e1051a39Sopenharmony_ci}
539