eng_dyn.c revision 279264
1/* crypto/engine/eng_dyn.c */ 2/* Written by Geoff Thorpe (geoff@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@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@cryptsoft.com). This product includes software written by Tim 55 * Hudson (tjh@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 */ 67static int dynamic_init(ENGINE *e); 68static int dynamic_finish(ENGINE *e); 69static int dynamic_ctrl(ENGINE *e, int cmd, long i, void *p, void (*f)(void)); 70/* Predeclare our context type */ 71typedef struct st_dynamic_data_ctx dynamic_data_ctx; 72/* The implementation for the important control command */ 73static 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 */ 84static const char *engine_dynamic_id = "dynamic"; 85static const char *engine_dynamic_name = "Dynamic engine loading support"; 86static 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 118/* Loading code stores state inside the ENGINE structure via the "ex_data" 119 * element. We load all our state into a single structure and use that as a 120 * single context in the "ex_data" stack. */ 121struct st_dynamic_data_ctx 122 { 123 /* The DSO object we load that supplies the ENGINE code */ 124 DSO *dynamic_dso; 125 /* The function pointer to the version checking shared library function */ 126 dynamic_v_check_fn v_check; 127 /* The function pointer to the engine-binding shared library function */ 128 dynamic_bind_engine bind_engine; 129 /* The default name/path for loading the shared library */ 130 const char *DYNAMIC_LIBNAME; 131 /* Whether to continue loading on a version check failure */ 132 int no_vcheck; 133 /* If non-NULL, stipulates the 'id' of the ENGINE to be loaded */ 134 const char *engine_id; 135 /* If non-zero, a successfully loaded ENGINE should be added to the internal 136 * ENGINE list. If 2, the add must succeed or the entire load should fail. */ 137 int list_add_value; 138 /* The symbol name for the version checking function */ 139 const char *DYNAMIC_F1; 140 /* The symbol name for the "initialise ENGINE structure" function */ 141 const char *DYNAMIC_F2; 142 /* Whether to never use 'dirs', use 'dirs' as a fallback, or only use 143 * 'dirs' for loading. Default is to use 'dirs' as a fallback. */ 144 int dir_load; 145 /* A stack of directories from which ENGINEs could be loaded */ 146 STACK_OF(OPENSSL_STRING) *dirs; 147 }; 148 149/* This is the "ex_data" index we obtain and reserve for use with our context 150 * structure. */ 151static int dynamic_ex_data_idx = -1; 152 153static void int_free_str(char *s) { OPENSSL_free(s); } 154/* Because our ex_data element may or may not get allocated depending on whether 155 * a "first-use" occurs before the ENGINE is freed, we have a memory leak 156 * problem to solve. We can't declare a "new" handler for the ex_data as we 157 * don't want a dynamic_data_ctx in *all* ENGINE structures of all types (this 158 * is a bug in the design of CRYPTO_EX_DATA). As such, we just declare a "free" 159 * handler and that will get called if an ENGINE is being destroyed and there 160 * was an ex_data element corresponding to our context type. */ 161static void dynamic_data_ctx_free_func(void *parent, void *ptr, 162 CRYPTO_EX_DATA *ad, int idx, long argl, void *argp) 163 { 164 if(ptr) 165 { 166 dynamic_data_ctx *ctx = (dynamic_data_ctx *)ptr; 167 if(ctx->dynamic_dso) 168 DSO_free(ctx->dynamic_dso); 169 if(ctx->DYNAMIC_LIBNAME) 170 OPENSSL_free((void*)ctx->DYNAMIC_LIBNAME); 171 if(ctx->engine_id) 172 OPENSSL_free((void*)ctx->engine_id); 173 if(ctx->dirs) 174 sk_OPENSSL_STRING_pop_free(ctx->dirs, int_free_str); 175 OPENSSL_free(ctx); 176 } 177 } 178 179/* Construct the per-ENGINE context. We create it blindly and then use a lock to 180 * check for a race - if so, all but one of the threads "racing" will have 181 * wasted their time. The alternative involves creating everything inside the 182 * lock which is far worse. */ 183static int dynamic_set_data_ctx(ENGINE *e, dynamic_data_ctx **ctx) 184 { 185 dynamic_data_ctx *c; 186 c = OPENSSL_malloc(sizeof(dynamic_data_ctx)); 187 if(!c) 188 { 189 ENGINEerr(ENGINE_F_DYNAMIC_SET_DATA_CTX,ERR_R_MALLOC_FAILURE); 190 return 0; 191 } 192 memset(c, 0, sizeof(dynamic_data_ctx)); 193 c->dynamic_dso = NULL; 194 c->v_check = NULL; 195 c->bind_engine = NULL; 196 c->DYNAMIC_LIBNAME = NULL; 197 c->no_vcheck = 0; 198 c->engine_id = NULL; 199 c->list_add_value = 0; 200 c->DYNAMIC_F1 = "v_check"; 201 c->DYNAMIC_F2 = "bind_engine"; 202 c->dir_load = 1; 203 c->dirs = sk_OPENSSL_STRING_new_null(); 204 if(!c->dirs) 205 { 206 ENGINEerr(ENGINE_F_DYNAMIC_SET_DATA_CTX,ERR_R_MALLOC_FAILURE); 207 OPENSSL_free(c); 208 return 0; 209 } 210 CRYPTO_w_lock(CRYPTO_LOCK_ENGINE); 211 if((*ctx = (dynamic_data_ctx *)ENGINE_get_ex_data(e, 212 dynamic_ex_data_idx)) == NULL) 213 { 214 /* Good, we're the first */ 215 ENGINE_set_ex_data(e, dynamic_ex_data_idx, c); 216 *ctx = c; 217 c = NULL; 218 } 219 CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE); 220 /* If we lost the race to set the context, c is non-NULL and *ctx is the 221 * context of the thread that won. */ 222 if(c) 223 OPENSSL_free(c); 224 return 1; 225 } 226 227/* This function retrieves the context structure from an ENGINE's "ex_data", or 228 * if it doesn't exist yet, sets it up. */ 229static dynamic_data_ctx *dynamic_get_data_ctx(ENGINE *e) 230 { 231 dynamic_data_ctx *ctx; 232 if(dynamic_ex_data_idx < 0) 233 { 234 /* Create and register the ENGINE ex_data, and associate our 235 * "free" function with it to ensure any allocated contexts get 236 * freed when an ENGINE goes underground. */ 237 int new_idx = ENGINE_get_ex_new_index(0, NULL, NULL, NULL, 238 dynamic_data_ctx_free_func); 239 if(new_idx == -1) 240 { 241 ENGINEerr(ENGINE_F_DYNAMIC_GET_DATA_CTX,ENGINE_R_NO_INDEX); 242 return NULL; 243 } 244 CRYPTO_w_lock(CRYPTO_LOCK_ENGINE); 245 /* Avoid a race by checking again inside this lock */ 246 if(dynamic_ex_data_idx < 0) 247 { 248 /* Good, someone didn't beat us to it */ 249 dynamic_ex_data_idx = new_idx; 250 new_idx = -1; 251 } 252 CRYPTO_w_unlock(CRYPTO_LOCK_ENGINE); 253 /* In theory we could "give back" the index here if 254 * (new_idx>-1), but it's not possible and wouldn't gain us much 255 * if it were. */ 256 } 257 ctx = (dynamic_data_ctx *)ENGINE_get_ex_data(e, dynamic_ex_data_idx); 258 /* Check if the context needs to be created */ 259 if((ctx == NULL) && !dynamic_set_data_ctx(e, &ctx)) 260 /* "set_data" will set errors if necessary */ 261 return NULL; 262 return ctx; 263 } 264 265static ENGINE *engine_dynamic(void) 266 { 267 ENGINE *ret = ENGINE_new(); 268 if(!ret) 269 return NULL; 270 if(!ENGINE_set_id(ret, engine_dynamic_id) || 271 !ENGINE_set_name(ret, engine_dynamic_name) || 272 !ENGINE_set_init_function(ret, dynamic_init) || 273 !ENGINE_set_finish_function(ret, dynamic_finish) || 274 !ENGINE_set_ctrl_function(ret, dynamic_ctrl) || 275 !ENGINE_set_flags(ret, ENGINE_FLAGS_BY_ID_COPY) || 276 !ENGINE_set_cmd_defns(ret, dynamic_cmd_defns)) 277 { 278 ENGINE_free(ret); 279 return NULL; 280 } 281 return ret; 282 } 283 284void ENGINE_load_dynamic(void) 285 { 286 ENGINE *toadd = engine_dynamic(); 287 if(!toadd) return; 288 ENGINE_add(toadd); 289 /* If the "add" worked, it gets a structural reference. So either way, 290 * we release our just-created reference. */ 291 ENGINE_free(toadd); 292 /* If the "add" didn't work, it was probably a conflict because it was 293 * already added (eg. someone calling ENGINE_load_blah then calling 294 * ENGINE_load_builtin_engines() perhaps). */ 295 ERR_clear_error(); 296 } 297 298static int dynamic_init(ENGINE *e) 299 { 300 /* We always return failure - the "dyanamic" engine itself can't be used 301 * for anything. */ 302 return 0; 303 } 304 305static int dynamic_finish(ENGINE *e) 306 { 307 /* This should never be called on account of "dynamic_init" always 308 * failing. */ 309 return 0; 310 } 311 312static int dynamic_ctrl(ENGINE *e, int cmd, long i, void *p, void (*f)(void)) 313 { 314 dynamic_data_ctx *ctx = dynamic_get_data_ctx(e); 315 int initialised; 316 317 if(!ctx) 318 { 319 ENGINEerr(ENGINE_F_DYNAMIC_CTRL,ENGINE_R_NOT_LOADED); 320 return 0; 321 } 322 initialised = ((ctx->dynamic_dso == NULL) ? 0 : 1); 323 /* All our control commands require the ENGINE to be uninitialised */ 324 if(initialised) 325 { 326 ENGINEerr(ENGINE_F_DYNAMIC_CTRL, 327 ENGINE_R_ALREADY_LOADED); 328 return 0; 329 } 330 switch(cmd) 331 { 332 case DYNAMIC_CMD_SO_PATH: 333 /* a NULL 'p' or a string of zero-length is the same thing */ 334 if(p && (strlen((const char *)p) < 1)) 335 p = NULL; 336 if(ctx->DYNAMIC_LIBNAME) 337 OPENSSL_free((void*)ctx->DYNAMIC_LIBNAME); 338 if(p) 339 ctx->DYNAMIC_LIBNAME = BUF_strdup(p); 340 else 341 ctx->DYNAMIC_LIBNAME = NULL; 342 return (ctx->DYNAMIC_LIBNAME ? 1 : 0); 343 case DYNAMIC_CMD_NO_VCHECK: 344 ctx->no_vcheck = ((i == 0) ? 0 : 1); 345 return 1; 346 case DYNAMIC_CMD_ID: 347 /* a NULL 'p' or a string of zero-length is the same thing */ 348 if(p && (strlen((const char *)p) < 1)) 349 p = NULL; 350 if(ctx->engine_id) 351 OPENSSL_free((void*)ctx->engine_id); 352 if(p) 353 ctx->engine_id = BUF_strdup(p); 354 else 355 ctx->engine_id = NULL; 356 return (ctx->engine_id ? 1 : 0); 357 case DYNAMIC_CMD_LIST_ADD: 358 if((i < 0) || (i > 2)) 359 { 360 ENGINEerr(ENGINE_F_DYNAMIC_CTRL, 361 ENGINE_R_INVALID_ARGUMENT); 362 return 0; 363 } 364 ctx->list_add_value = (int)i; 365 return 1; 366 case DYNAMIC_CMD_LOAD: 367 return dynamic_load(e, ctx); 368 case DYNAMIC_CMD_DIR_LOAD: 369 if((i < 0) || (i > 2)) 370 { 371 ENGINEerr(ENGINE_F_DYNAMIC_CTRL, 372 ENGINE_R_INVALID_ARGUMENT); 373 return 0; 374 } 375 ctx->dir_load = (int)i; 376 return 1; 377 case DYNAMIC_CMD_DIR_ADD: 378 /* a NULL 'p' or a string of zero-length is the same thing */ 379 if(!p || (strlen((const char *)p) < 1)) 380 { 381 ENGINEerr(ENGINE_F_DYNAMIC_CTRL, 382 ENGINE_R_INVALID_ARGUMENT); 383 return 0; 384 } 385 { 386 char *tmp_str = BUF_strdup(p); 387 if(!tmp_str) 388 { 389 ENGINEerr(ENGINE_F_DYNAMIC_CTRL, 390 ERR_R_MALLOC_FAILURE); 391 return 0; 392 } 393 sk_OPENSSL_STRING_insert(ctx->dirs, tmp_str, -1); 394 } 395 return 1; 396 default: 397 break; 398 } 399 ENGINEerr(ENGINE_F_DYNAMIC_CTRL,ENGINE_R_CTRL_COMMAND_NOT_IMPLEMENTED); 400 return 0; 401 } 402 403static int int_load(dynamic_data_ctx *ctx) 404 { 405 int num, loop; 406 /* Unless told not to, try a direct load */ 407 if((ctx->dir_load != 2) && (DSO_load(ctx->dynamic_dso, 408 ctx->DYNAMIC_LIBNAME, NULL, 0)) != NULL) 409 return 1; 410 /* If we're not allowed to use 'dirs' or we have none, fail */ 411 if(!ctx->dir_load || (num = sk_OPENSSL_STRING_num(ctx->dirs)) < 1) 412 return 0; 413 for(loop = 0; loop < num; loop++) 414 { 415 const char *s = sk_OPENSSL_STRING_value(ctx->dirs, loop); 416 char *merge = DSO_merge(ctx->dynamic_dso, ctx->DYNAMIC_LIBNAME, s); 417 if(!merge) 418 return 0; 419 if(DSO_load(ctx->dynamic_dso, merge, NULL, 0)) 420 { 421 /* Found what we're looking for */ 422 OPENSSL_free(merge); 423 return 1; 424 } 425 OPENSSL_free(merge); 426 } 427 return 0; 428 } 429 430static int dynamic_load(ENGINE *e, dynamic_data_ctx *ctx) 431 { 432 ENGINE cpy; 433 dynamic_fns fns; 434 435 if(!ctx->dynamic_dso) 436 ctx->dynamic_dso = DSO_new(); 437 if(!ctx->DYNAMIC_LIBNAME) 438 { 439 if(!ctx->engine_id) 440 return 0; 441 ctx->DYNAMIC_LIBNAME = 442 DSO_convert_filename(ctx->dynamic_dso, ctx->engine_id); 443 } 444 if(!int_load(ctx)) 445 { 446 ENGINEerr(ENGINE_F_DYNAMIC_LOAD, 447 ENGINE_R_DSO_NOT_FOUND); 448 DSO_free(ctx->dynamic_dso); 449 ctx->dynamic_dso = NULL; 450 return 0; 451 } 452 /* We have to find a bind function otherwise it'll always end badly */ 453 if(!(ctx->bind_engine = (dynamic_bind_engine)DSO_bind_func( 454 ctx->dynamic_dso, ctx->DYNAMIC_F2))) 455 { 456 ctx->bind_engine = NULL; 457 DSO_free(ctx->dynamic_dso); 458 ctx->dynamic_dso = NULL; 459 ENGINEerr(ENGINE_F_DYNAMIC_LOAD, 460 ENGINE_R_DSO_FAILURE); 461 return 0; 462 } 463 /* Do we perform version checking? */ 464 if(!ctx->no_vcheck) 465 { 466 unsigned long vcheck_res = 0; 467 /* Now we try to find a version checking function and decide how 468 * to cope with failure if/when it fails. */ 469 ctx->v_check = (dynamic_v_check_fn)DSO_bind_func( 470 ctx->dynamic_dso, ctx->DYNAMIC_F1); 471 if(ctx->v_check) 472 vcheck_res = ctx->v_check(OSSL_DYNAMIC_VERSION); 473 /* We fail if the version checker veto'd the load *or* if it is 474 * deferring to us (by returning its version) and we think it is 475 * too old. */ 476 if(vcheck_res < OSSL_DYNAMIC_OLDEST) 477 { 478 /* Fail */ 479 ctx->bind_engine = NULL; 480 ctx->v_check = NULL; 481 DSO_free(ctx->dynamic_dso); 482 ctx->dynamic_dso = NULL; 483 ENGINEerr(ENGINE_F_DYNAMIC_LOAD, 484 ENGINE_R_VERSION_INCOMPATIBILITY); 485 return 0; 486 } 487 } 488 /* First binary copy the ENGINE structure so that we can roll back if 489 * the hand-over fails */ 490 memcpy(&cpy, e, sizeof(ENGINE)); 491 /* Provide the ERR, "ex_data", memory, and locking callbacks so the 492 * loaded library uses our state rather than its own. FIXME: As noted in 493 * engine.h, much of this would be simplified if each area of code 494 * provided its own "summary" structure of all related callbacks. It 495 * would also increase opaqueness. */ 496 fns.static_state = ENGINE_get_static_state(); 497 fns.err_fns = ERR_get_implementation(); 498 fns.ex_data_fns = CRYPTO_get_ex_data_implementation(); 499 CRYPTO_get_mem_functions(&fns.mem_fns.malloc_cb, 500 &fns.mem_fns.realloc_cb, 501 &fns.mem_fns.free_cb); 502 fns.lock_fns.lock_locking_cb = CRYPTO_get_locking_callback(); 503 fns.lock_fns.lock_add_lock_cb = CRYPTO_get_add_lock_callback(); 504 fns.lock_fns.dynlock_create_cb = CRYPTO_get_dynlock_create_callback(); 505 fns.lock_fns.dynlock_lock_cb = CRYPTO_get_dynlock_lock_callback(); 506 fns.lock_fns.dynlock_destroy_cb = CRYPTO_get_dynlock_destroy_callback(); 507 /* Now that we've loaded the dynamic engine, make sure no "dynamic" 508 * ENGINE elements will show through. */ 509 engine_set_all_null(e); 510 511 /* Try to bind the ENGINE onto our own ENGINE structure */ 512 if(!ctx->bind_engine(e, ctx->engine_id, &fns)) 513 { 514 ctx->bind_engine = NULL; 515 ctx->v_check = NULL; 516 DSO_free(ctx->dynamic_dso); 517 ctx->dynamic_dso = NULL; 518 ENGINEerr(ENGINE_F_DYNAMIC_LOAD,ENGINE_R_INIT_FAILED); 519 /* Copy the original ENGINE structure back */ 520 memcpy(e, &cpy, sizeof(ENGINE)); 521 return 0; 522 } 523 /* Do we try to add this ENGINE to the internal list too? */ 524 if(ctx->list_add_value > 0) 525 { 526 if(!ENGINE_add(e)) 527 { 528 /* Do we tolerate this or fail? */ 529 if(ctx->list_add_value > 1) 530 { 531 /* Fail - NB: By this time, it's too late to 532 * rollback, and trying to do so allows the 533 * bind_engine() code to have created leaks. We 534 * just have to fail where we are, after the 535 * ENGINE has changed. */ 536 ENGINEerr(ENGINE_F_DYNAMIC_LOAD, 537 ENGINE_R_CONFLICTING_ENGINE_ID); 538 return 0; 539 } 540 /* Tolerate */ 541 ERR_clear_error(); 542 } 543 } 544 return 1; 545 } 546