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