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