openssl/crypto/engine/eng_lib.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

310 lines
6.9 KiB
C
Raw Normal View History

2001-09-07 12:14:48 +08:00
/*
* Copyright 2001-2023 The OpenSSL Project Authors. All Rights Reserved.
2001-09-07 12:14:48 +08:00
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
2001-09-07 12:14:48 +08:00
*/
#include "internal/e_os.h"
#include "eng_local.h"
#include <openssl/rand.h>
#include "internal/refcount.h"
2001-09-07 12:14:48 +08:00
CRYPTO_RWLOCK *global_engine_lock;
CRYPTO_ONCE engine_lock_init = CRYPTO_ONCE_STATIC_INIT;
/* The "new"/"free" stuff first */
2001-09-07 12:14:48 +08:00
DEFINE_RUN_ONCE(do_engine_lock_init)
{
Revert the crypto "global lock" implementation Conceptually, this is a squashed version of: Revert "Address feedback" This reverts commit 75551e07bd2339dfea06ef1d31d69929e13a4495. and Revert "Add CRYPTO_thread_glock_new" This reverts commit ed6b2c7938ec6f07b15745d4183afc276e74c6dd. But there were some intervening commits that made neither revert apply cleanly, so instead do it all as one shot. The crypto global locks were an attempt to cope with the awkward POSIX semantics for pthread_atfork(); its documentation (the "RATIONALE" section) indicates that the expected usage is to have the prefork handler lock all "global" locks, and the parent and child handlers release those locks, to ensure that forking happens with a consistent (lock) state. However, the set of functions available in the child process is limited to async-signal-safe functions, and pthread_mutex_unlock() is not on the list of async-signal-safe functions! The only synchronization primitives that are async-signal-safe are the semaphore primitives, which are not really appropriate for general-purpose usage. However, the state consistency problem that the global locks were attempting to solve is not actually a serious problem, particularly for OpenSSL. That is, we can consider four cases of forking application that might use OpenSSL: (1) Single-threaded, does not call into OpenSSL in the child (e.g., the child calls exec() immediately) For this class of process, no locking is needed at all, since there is only ever a single thread of execution and the only reentrancy is due to signal handlers (which are themselves limited to async-signal-safe operation and should not be doing much work at all). (2) Single-threaded, calls into OpenSSL after fork() The application must ensure that it does not fork() with an unexpected lock held (that is, one that would get unlocked in the parent but accidentally remain locked in the child and cause deadlock). Since OpenSSL does not expose any of its internal locks to the application and the application is single-threaded, the OpenSSL internal locks will be unlocked for the fork(), and the state will be consistent. (OpenSSL will need to reseed its PRNG in the child, but that is an orthogonal issue.) If the application makes use of locks from libcrypto, proper handling for those locks is the responsibility of the application, as for any other locking primitive that is available for application programming. (3) Multi-threaded, does not call into OpenSSL after fork() As for (1), the OpenSSL state is only relevant in the parent, so no particular fork()-related handling is needed. The internal locks are relevant, but there is no interaction with the child to consider. (4) Multi-threaded, calls into OpenSSL after fork() This is the case where the pthread_atfork() hooks to ensure that all global locks are in a known state across fork() would come into play, per the above discussion. However, these "calls into OpenSSL after fork()" are still subject to the restriction to async-signal-safe functions. Since OpenSSL uses all sorts of locking and libc functions that are not on the list of safe functions (e.g., malloc()), this case is not currently usable and is unlikely to ever be usable, independently of the locking situation. So, there is no need to go through contortions to attempt to support this case in the one small area of locking interaction with fork(). In light of the above analysis (thanks @davidben and @achernya), go back to the simpler implementation that does not need to distinguish "library-global" locks or to have complicated atfork handling for locks. Reviewed-by: Kurt Roeckx <kurt@roeckx.be> Reviewed-by: Matthias St. Pierre <Matthias.St.Pierre@ncp-e.com> (Merged from https://github.com/openssl/openssl/pull/5089)
2018-01-16 23:49:54 +08:00
global_engine_lock = CRYPTO_THREAD_lock_new();
return global_engine_lock != NULL;
}
ENGINE *ENGINE_new(void)
2001-09-07 12:14:48 +08:00
{
ENGINE *ret;
if (!RUN_ONCE(&engine_lock_init, do_engine_lock_init)) {
/* Maybe this should be raised in do_engine_lock_init() */
ERR_raise(ERR_LIB_ENGINE, ERR_R_CRYPTO_LIB);
return 0;
2001-09-07 12:14:48 +08:00
}
if ((ret = OPENSSL_zalloc(sizeof(*ret))) == NULL)
return NULL;
if (!CRYPTO_NEW_REF(&ret->struct_ref, 1)) {
OPENSSL_free(ret);
return NULL;
}
ENGINE_REF_PRINT(ret, 0, 1);
if (!CRYPTO_new_ex_data(CRYPTO_EX_INDEX_ENGINE, ret, &ret->ex_data)) {
CRYPTO_FREE_REF(&ret->struct_ref);
OPENSSL_free(ret);
return NULL;
}
return ret;
2001-09-07 12:14:48 +08:00
}
/*
* Placed here (close proximity to ENGINE_new) so that modifications to the
* elements of the ENGINE structure are more likely to be caught and changed
* here.
*/
void engine_set_all_null(ENGINE *e)
{
e->id = NULL;
e->name = NULL;
e->rsa_meth = NULL;
e->dsa_meth = NULL;
e->dh_meth = NULL;
e->rand_meth = NULL;
e->ciphers = NULL;
e->digests = NULL;
e->destroy = NULL;
e->init = NULL;
e->finish = NULL;
e->ctrl = NULL;
e->load_privkey = NULL;
e->load_pubkey = NULL;
e->cmd_defns = NULL;
e->flags = 0;
e->dynamic_id = NULL;
}
int engine_free_util(ENGINE *e, int not_locked)
2001-09-07 12:14:48 +08:00
{
int i;
if (e == NULL)
return 1;
CRYPTO_DOWN_REF(&e->struct_ref, &i);
ENGINE_REF_PRINT(e, 0, -1);
if (i > 0)
return 1;
REF_ASSERT_ISNT(i < 0);
/* Free up any dynamically allocated public key methods */
engine_pkey_meths_free(e);
engine_pkey_asn1_meths_free(e);
/*
* Give the ENGINE a chance to do any structural cleanup corresponding to
* allocation it did in its constructor (eg. unload error strings)
*/
if (e->destroy)
e->destroy(e);
engine_remove_dynamic_id(e, not_locked);
CRYPTO_free_ex_data(CRYPTO_EX_INDEX_ENGINE, e, &e->ex_data);
CRYPTO_FREE_REF(&e->struct_ref);
OPENSSL_free(e);
return 1;
2001-09-07 12:14:48 +08:00
}
int ENGINE_free(ENGINE *e)
2001-09-07 12:14:48 +08:00
{
return engine_free_util(e, 1);
2001-09-07 12:14:48 +08:00
}
/* Cleanup stuff */
2001-09-07 12:14:48 +08:00
/*
* engine_cleanup_int() is coded such that anything that does work that will
* need cleanup can register a "cleanup" callback here. That way we don't get
* linker bloat by referring to all *possible* cleanups, but any linker bloat
* into code "X" will cause X's cleanup function to end up here.
*/
static STACK_OF(ENGINE_CLEANUP_ITEM) *cleanup_stack = NULL;
static int int_cleanup_check(int create)
2001-09-07 12:14:48 +08:00
{
if (cleanup_stack)
return 1;
if (!create)
return 0;
cleanup_stack = sk_ENGINE_CLEANUP_ITEM_new_null();
return (cleanup_stack ? 1 : 0);
2001-09-07 12:14:48 +08:00
}
static ENGINE_CLEANUP_ITEM *int_cleanup_item(ENGINE_CLEANUP_CB *cb)
{
ENGINE_CLEANUP_ITEM *item;
if ((item = OPENSSL_malloc(sizeof(*item))) == NULL)
return NULL;
item->cb = cb;
return item;
}
int engine_cleanup_add_first(ENGINE_CLEANUP_CB *cb)
2001-09-07 12:14:48 +08:00
{
ENGINE_CLEANUP_ITEM *item;
if (!int_cleanup_check(1))
return 0;
item = int_cleanup_item(cb);
if (item != NULL) {
if (sk_ENGINE_CLEANUP_ITEM_insert(cleanup_stack, item, 0))
return 1;
OPENSSL_free(item);
}
return 0;
2001-09-07 12:14:48 +08:00
}
int engine_cleanup_add_last(ENGINE_CLEANUP_CB *cb)
2001-09-07 12:14:48 +08:00
{
ENGINE_CLEANUP_ITEM *item;
if (!int_cleanup_check(1))
return 0;
item = int_cleanup_item(cb);
if (item != NULL) {
if (sk_ENGINE_CLEANUP_ITEM_push(cleanup_stack, item) > 0)
return 1;
OPENSSL_free(item);
}
return 0;
2001-09-07 12:14:48 +08:00
}
/* The API function that performs all cleanup */
static void engine_cleanup_cb_free(ENGINE_CLEANUP_ITEM *item)
{
(*(item->cb)) ();
2001-10-02 01:20:28 +08:00
OPENSSL_free(item);
}
void engine_cleanup_int(void)
2001-09-07 12:14:48 +08:00
{
if (int_cleanup_check(0)) {
sk_ENGINE_CLEANUP_ITEM_pop_free(cleanup_stack,
engine_cleanup_cb_free);
cleanup_stack = NULL;
2001-09-07 12:14:48 +08:00
}
CRYPTO_THREAD_lock_free(global_engine_lock);
global_engine_lock = NULL;
2001-09-07 12:14:48 +08:00
}
/* Now the "ex_data" support */
int ENGINE_set_ex_data(ENGINE *e, int idx, void *arg)
2001-09-07 12:14:48 +08:00
{
return CRYPTO_set_ex_data(&e->ex_data, idx, arg);
2001-09-07 12:14:48 +08:00
}
void *ENGINE_get_ex_data(const ENGINE *e, int idx)
{
return CRYPTO_get_ex_data(&e->ex_data, idx);
}
2001-09-07 12:14:48 +08:00
/*
* Functions to get/set an ENGINE's elements - mainly to avoid exposing the
* ENGINE structure itself.
*/
2001-09-07 12:14:48 +08:00
int ENGINE_set_id(ENGINE *e, const char *id)
2001-09-07 12:14:48 +08:00
{
if (id == NULL) {
ERR_raise(ERR_LIB_ENGINE, ERR_R_PASSED_NULL_PARAMETER);
2001-09-07 12:14:48 +08:00
return 0;
}
e->id = id;
return 1;
2001-09-07 12:14:48 +08:00
}
int ENGINE_set_name(ENGINE *e, const char *name)
2001-09-07 12:14:48 +08:00
{
if (name == NULL) {
ERR_raise(ERR_LIB_ENGINE, ERR_R_PASSED_NULL_PARAMETER);
return 0;
2001-09-07 12:14:48 +08:00
}
e->name = name;
return 1;
2001-09-07 12:14:48 +08:00
}
int ENGINE_set_destroy_function(ENGINE *e, ENGINE_GEN_INT_FUNC_PTR destroy_f)
2001-09-07 12:14:48 +08:00
{
e->destroy = destroy_f;
return 1;
2001-09-07 12:14:48 +08:00
}
int ENGINE_set_init_function(ENGINE *e, ENGINE_GEN_INT_FUNC_PTR init_f)
2001-09-07 12:14:48 +08:00
{
e->init = init_f;
return 1;
2001-09-07 12:14:48 +08:00
}
int ENGINE_set_finish_function(ENGINE *e, ENGINE_GEN_INT_FUNC_PTR finish_f)
2001-09-07 12:14:48 +08:00
{
e->finish = finish_f;
return 1;
2001-09-07 12:14:48 +08:00
}
int ENGINE_set_ctrl_function(ENGINE *e, ENGINE_CTRL_FUNC_PTR ctrl_f)
2001-09-07 12:14:48 +08:00
{
e->ctrl = ctrl_f;
return 1;
2001-09-07 12:14:48 +08:00
}
int ENGINE_set_flags(ENGINE *e, int flags)
2001-09-07 12:14:48 +08:00
{
e->flags = flags;
return 1;
2001-09-07 12:14:48 +08:00
}
int ENGINE_set_cmd_defns(ENGINE *e, const ENGINE_CMD_DEFN *defns)
2001-09-07 12:14:48 +08:00
{
e->cmd_defns = defns;
2001-09-07 12:14:48 +08:00
return 1;
}
const char *ENGINE_get_id(const ENGINE *e)
2001-09-07 12:14:48 +08:00
{
return e->id;
2001-09-07 12:14:48 +08:00
}
const char *ENGINE_get_name(const ENGINE *e)
2001-09-07 12:14:48 +08:00
{
return e->name;
2001-09-07 12:14:48 +08:00
}
ENGINE_GEN_INT_FUNC_PTR ENGINE_get_destroy_function(const ENGINE *e)
2001-09-07 12:14:48 +08:00
{
return e->destroy;
2001-09-07 12:14:48 +08:00
}
ENGINE_GEN_INT_FUNC_PTR ENGINE_get_init_function(const ENGINE *e)
2001-09-07 12:14:48 +08:00
{
return e->init;
2001-09-07 12:14:48 +08:00
}
ENGINE_GEN_INT_FUNC_PTR ENGINE_get_finish_function(const ENGINE *e)
2001-09-07 12:14:48 +08:00
{
return e->finish;
2001-09-07 12:14:48 +08:00
}
ENGINE_CTRL_FUNC_PTR ENGINE_get_ctrl_function(const ENGINE *e)
2001-09-07 12:14:48 +08:00
{
return e->ctrl;
2001-09-07 12:14:48 +08:00
}
int ENGINE_get_flags(const ENGINE *e)
2001-09-07 12:14:48 +08:00
{
return e->flags;
2001-09-07 12:14:48 +08:00
}
const ENGINE_CMD_DEFN *ENGINE_get_cmd_defns(const ENGINE *e)
2001-09-07 12:14:48 +08:00
{
return e->cmd_defns;
2001-09-07 12:14:48 +08:00
}
/*
* eng_lib.o is pretty much linked into anything that touches ENGINE already,
* so put the "static_state" hack here.
*/
static int internal_static_hack = 0;
void *ENGINE_get_static_state(void)
{
return &internal_static_hack;
}