diff options
Diffstat (limited to 'crypto/context.c')
| -rw-r--r-- | crypto/context.c | 510 |
1 files changed, 510 insertions, 0 deletions
diff --git a/crypto/context.c b/crypto/context.c new file mode 100644 index 000000000000..548665fba265 --- /dev/null +++ b/crypto/context.c @@ -0,0 +1,510 @@ +/* + * Copyright 2019-2022 The OpenSSL Project Authors. All Rights Reserved. + * + * 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 + */ + +#include "crypto/cryptlib.h" +#include <openssl/conf.h> +#include "internal/thread_once.h" +#include "internal/property.h" +#include "internal/core.h" +#include "internal/bio.h" +#include "internal/provider.h" +#include "crypto/ctype.h" +#include "crypto/rand.h" + +struct ossl_lib_ctx_onfree_list_st { + ossl_lib_ctx_onfree_fn *fn; + struct ossl_lib_ctx_onfree_list_st *next; +}; + +struct ossl_lib_ctx_st { + CRYPTO_RWLOCK *lock; + CRYPTO_EX_DATA data; + + /* + * For most data in the OSSL_LIB_CTX we just use ex_data to store it. But + * that doesn't work for ex_data itself - so we store that directly. + */ + OSSL_EX_DATA_GLOBAL global; + + /* Map internal static indexes to dynamically created indexes */ + int dyn_indexes[OSSL_LIB_CTX_MAX_INDEXES]; + + /* Keep a separate lock for each index */ + CRYPTO_RWLOCK *index_locks[OSSL_LIB_CTX_MAX_INDEXES]; + + CRYPTO_RWLOCK *oncelock; + int run_once_done[OSSL_LIB_CTX_MAX_RUN_ONCE]; + int run_once_ret[OSSL_LIB_CTX_MAX_RUN_ONCE]; + struct ossl_lib_ctx_onfree_list_st *onfreelist; + unsigned int ischild:1; +}; + +int ossl_lib_ctx_write_lock(OSSL_LIB_CTX *ctx) +{ + return CRYPTO_THREAD_write_lock(ossl_lib_ctx_get_concrete(ctx)->lock); +} + +int ossl_lib_ctx_read_lock(OSSL_LIB_CTX *ctx) +{ + return CRYPTO_THREAD_read_lock(ossl_lib_ctx_get_concrete(ctx)->lock); +} + +int ossl_lib_ctx_unlock(OSSL_LIB_CTX *ctx) +{ + return CRYPTO_THREAD_unlock(ossl_lib_ctx_get_concrete(ctx)->lock); +} + +int ossl_lib_ctx_is_child(OSSL_LIB_CTX *ctx) +{ + ctx = ossl_lib_ctx_get_concrete(ctx); + + if (ctx == NULL) + return 0; + return ctx->ischild; +} + +static int context_init(OSSL_LIB_CTX *ctx) +{ + size_t i; + int exdata_done = 0; + + ctx->lock = CRYPTO_THREAD_lock_new(); + if (ctx->lock == NULL) + return 0; + + ctx->oncelock = CRYPTO_THREAD_lock_new(); + if (ctx->oncelock == NULL) + goto err; + + for (i = 0; i < OSSL_LIB_CTX_MAX_INDEXES; i++) { + ctx->index_locks[i] = CRYPTO_THREAD_lock_new(); + ctx->dyn_indexes[i] = -1; + if (ctx->index_locks[i] == NULL) + goto err; + } + + /* OSSL_LIB_CTX is built on top of ex_data so we initialise that directly */ + if (!ossl_do_ex_data_init(ctx)) + goto err; + exdata_done = 1; + + if (!ossl_crypto_new_ex_data_ex(ctx, CRYPTO_EX_INDEX_OSSL_LIB_CTX, NULL, + &ctx->data)) + goto err; + + /* Everything depends on properties, so we also pre-initialise that */ + if (!ossl_property_parse_init(ctx)) + goto err; + + return 1; + err: + if (exdata_done) + ossl_crypto_cleanup_all_ex_data_int(ctx); + for (i = 0; i < OSSL_LIB_CTX_MAX_INDEXES; i++) + CRYPTO_THREAD_lock_free(ctx->index_locks[i]); + CRYPTO_THREAD_lock_free(ctx->oncelock); + CRYPTO_THREAD_lock_free(ctx->lock); + memset(ctx, '\0', sizeof(*ctx)); + return 0; +} + +static int context_deinit(OSSL_LIB_CTX *ctx) +{ + struct ossl_lib_ctx_onfree_list_st *tmp, *onfree; + int i; + + if (ctx == NULL) + return 1; + + ossl_ctx_thread_stop(ctx); + + onfree = ctx->onfreelist; + while (onfree != NULL) { + onfree->fn(ctx); + tmp = onfree; + onfree = onfree->next; + OPENSSL_free(tmp); + } + CRYPTO_free_ex_data(CRYPTO_EX_INDEX_OSSL_LIB_CTX, NULL, &ctx->data); + ossl_crypto_cleanup_all_ex_data_int(ctx); + for (i = 0; i < OSSL_LIB_CTX_MAX_INDEXES; i++) + CRYPTO_THREAD_lock_free(ctx->index_locks[i]); + + CRYPTO_THREAD_lock_free(ctx->oncelock); + CRYPTO_THREAD_lock_free(ctx->lock); + ctx->lock = NULL; + return 1; +} + +#ifndef FIPS_MODULE +/* The default default context */ +static OSSL_LIB_CTX default_context_int; + +static CRYPTO_ONCE default_context_init = CRYPTO_ONCE_STATIC_INIT; +static CRYPTO_THREAD_LOCAL default_context_thread_local; + +DEFINE_RUN_ONCE_STATIC(default_context_do_init) +{ + return CRYPTO_THREAD_init_local(&default_context_thread_local, NULL) + && context_init(&default_context_int); +} + +void ossl_lib_ctx_default_deinit(void) +{ + context_deinit(&default_context_int); + CRYPTO_THREAD_cleanup_local(&default_context_thread_local); +} + +static OSSL_LIB_CTX *get_thread_default_context(void) +{ + if (!RUN_ONCE(&default_context_init, default_context_do_init)) + return NULL; + + return CRYPTO_THREAD_get_local(&default_context_thread_local); +} + +static OSSL_LIB_CTX *get_default_context(void) +{ + OSSL_LIB_CTX *current_defctx = get_thread_default_context(); + + if (current_defctx == NULL) + current_defctx = &default_context_int; + return current_defctx; +} + +static int set_default_context(OSSL_LIB_CTX *defctx) +{ + if (defctx == &default_context_int) + defctx = NULL; + + return CRYPTO_THREAD_set_local(&default_context_thread_local, defctx); +} +#endif + +OSSL_LIB_CTX *OSSL_LIB_CTX_new(void) +{ + OSSL_LIB_CTX *ctx = OPENSSL_zalloc(sizeof(*ctx)); + + if (ctx != NULL && !context_init(ctx)) { + OPENSSL_free(ctx); + ctx = NULL; + } + return ctx; +} + +#ifndef FIPS_MODULE +OSSL_LIB_CTX *OSSL_LIB_CTX_new_from_dispatch(const OSSL_CORE_HANDLE *handle, + const OSSL_DISPATCH *in) +{ + OSSL_LIB_CTX *ctx = OSSL_LIB_CTX_new(); + + if (ctx == NULL) + return NULL; + + if (!ossl_bio_init_core(ctx, in)) { + OSSL_LIB_CTX_free(ctx); + return NULL; + } + + return ctx; +} + +OSSL_LIB_CTX *OSSL_LIB_CTX_new_child(const OSSL_CORE_HANDLE *handle, + const OSSL_DISPATCH *in) +{ + OSSL_LIB_CTX *ctx = OSSL_LIB_CTX_new_from_dispatch(handle, in); + + if (ctx == NULL) + return NULL; + + if (!ossl_provider_init_as_child(ctx, handle, in)) { + OSSL_LIB_CTX_free(ctx); + return NULL; + } + ctx->ischild = 1; + + return ctx; +} + +int OSSL_LIB_CTX_load_config(OSSL_LIB_CTX *ctx, const char *config_file) +{ + return CONF_modules_load_file_ex(ctx, config_file, NULL, 0) > 0; +} +#endif + +void OSSL_LIB_CTX_free(OSSL_LIB_CTX *ctx) +{ + if (ossl_lib_ctx_is_default(ctx)) + return; + +#ifndef FIPS_MODULE + if (ctx->ischild) + ossl_provider_deinit_child(ctx); +#endif + context_deinit(ctx); + OPENSSL_free(ctx); +} + +#ifndef FIPS_MODULE +OSSL_LIB_CTX *OSSL_LIB_CTX_get0_global_default(void) +{ + if (!RUN_ONCE(&default_context_init, default_context_do_init)) + return NULL; + + return &default_context_int; +} + +OSSL_LIB_CTX *OSSL_LIB_CTX_set0_default(OSSL_LIB_CTX *libctx) +{ + OSSL_LIB_CTX *current_defctx; + + if ((current_defctx = get_default_context()) != NULL) { + if (libctx != NULL) + set_default_context(libctx); + return current_defctx; + } + + return NULL; +} + +void ossl_release_default_drbg_ctx(void) +{ + int dynidx = default_context_int.dyn_indexes[OSSL_LIB_CTX_DRBG_INDEX]; + + /* early release of the DRBG in global default libctx, no locking */ + if (dynidx != -1) { + void *data; + + data = CRYPTO_get_ex_data(&default_context_int.data, dynidx); + ossl_rand_ctx_free(data); + CRYPTO_set_ex_data(&default_context_int.data, dynidx, NULL); + } +} +#endif + +OSSL_LIB_CTX *ossl_lib_ctx_get_concrete(OSSL_LIB_CTX *ctx) +{ +#ifndef FIPS_MODULE + if (ctx == NULL) + return get_default_context(); +#endif + return ctx; +} + +int ossl_lib_ctx_is_default(OSSL_LIB_CTX *ctx) +{ +#ifndef FIPS_MODULE + if (ctx == NULL || ctx == get_default_context()) + return 1; +#endif + return 0; +} + +int ossl_lib_ctx_is_global_default(OSSL_LIB_CTX *ctx) +{ +#ifndef FIPS_MODULE + if (ossl_lib_ctx_get_concrete(ctx) == &default_context_int) + return 1; +#endif + return 0; +} + +static void ossl_lib_ctx_generic_new(void *parent_ign, void *ptr_ign, + CRYPTO_EX_DATA *ad, int index, + long argl_ign, void *argp) +{ + const OSSL_LIB_CTX_METHOD *meth = argp; + OSSL_LIB_CTX *ctx = ossl_crypto_ex_data_get_ossl_lib_ctx(ad); + void *ptr = meth->new_func(ctx); + + if (ptr != NULL) { + if (!CRYPTO_THREAD_write_lock(ctx->lock)) + /* + * Can't return something, so best to hope that something will + * fail later. :( + */ + return; + CRYPTO_set_ex_data(ad, index, ptr); + CRYPTO_THREAD_unlock(ctx->lock); + } +} +static void ossl_lib_ctx_generic_free(void *parent_ign, void *ptr, + CRYPTO_EX_DATA *ad, int index, + long argl_ign, void *argp) +{ + const OSSL_LIB_CTX_METHOD *meth = argp; + + meth->free_func(ptr); +} + +static int ossl_lib_ctx_init_index(OSSL_LIB_CTX *ctx, int static_index, + const OSSL_LIB_CTX_METHOD *meth) +{ + int idx; + + ctx = ossl_lib_ctx_get_concrete(ctx); + if (ctx == NULL) + return 0; + + idx = ossl_crypto_get_ex_new_index_ex(ctx, CRYPTO_EX_INDEX_OSSL_LIB_CTX, 0, + (void *)meth, + ossl_lib_ctx_generic_new, + NULL, ossl_lib_ctx_generic_free, + meth->priority); + if (idx < 0) + return 0; + + ctx->dyn_indexes[static_index] = idx; + return 1; +} + +void *ossl_lib_ctx_get_data(OSSL_LIB_CTX *ctx, int index, + const OSSL_LIB_CTX_METHOD *meth) +{ + void *data = NULL; + int dynidx; + + ctx = ossl_lib_ctx_get_concrete(ctx); + if (ctx == NULL) + return NULL; + + if (!CRYPTO_THREAD_read_lock(ctx->lock)) + return NULL; + dynidx = ctx->dyn_indexes[index]; + CRYPTO_THREAD_unlock(ctx->lock); + + if (dynidx != -1) { + if (!CRYPTO_THREAD_read_lock(ctx->index_locks[index])) + return NULL; + if (!CRYPTO_THREAD_read_lock(ctx->lock)) { + CRYPTO_THREAD_unlock(ctx->index_locks[index]); + return NULL; + } + data = CRYPTO_get_ex_data(&ctx->data, dynidx); + CRYPTO_THREAD_unlock(ctx->lock); + CRYPTO_THREAD_unlock(ctx->index_locks[index]); + return data; + } + + if (!CRYPTO_THREAD_write_lock(ctx->index_locks[index])) + return NULL; + if (!CRYPTO_THREAD_write_lock(ctx->lock)) { + CRYPTO_THREAD_unlock(ctx->index_locks[index]); + return NULL; + } + + dynidx = ctx->dyn_indexes[index]; + if (dynidx != -1) { + data = CRYPTO_get_ex_data(&ctx->data, dynidx); + CRYPTO_THREAD_unlock(ctx->lock); + CRYPTO_THREAD_unlock(ctx->index_locks[index]); + return data; + } + + if (!ossl_lib_ctx_init_index(ctx, index, meth)) { + CRYPTO_THREAD_unlock(ctx->lock); + CRYPTO_THREAD_unlock(ctx->index_locks[index]); + return NULL; + } + + CRYPTO_THREAD_unlock(ctx->lock); + + /* + * The alloc call ensures there's a value there. We release the ctx->lock + * for this, because the allocation itself may recursively call + * ossl_lib_ctx_get_data for other indexes (never this one). The allocation + * will itself aquire the ctx->lock when it actually comes to store the + * allocated data (see ossl_lib_ctx_generic_new() above). We call + * ossl_crypto_alloc_ex_data_intern() here instead of CRYPTO_alloc_ex_data(). + * They do the same thing except that the latter calls CRYPTO_get_ex_data() + * as well - which we must not do without holding the ctx->lock. + */ + if (ossl_crypto_alloc_ex_data_intern(CRYPTO_EX_INDEX_OSSL_LIB_CTX, NULL, + &ctx->data, ctx->dyn_indexes[index])) { + if (!CRYPTO_THREAD_read_lock(ctx->lock)) + goto end; + data = CRYPTO_get_ex_data(&ctx->data, ctx->dyn_indexes[index]); + CRYPTO_THREAD_unlock(ctx->lock); + } + +end: + CRYPTO_THREAD_unlock(ctx->index_locks[index]); + return data; +} + +OSSL_EX_DATA_GLOBAL *ossl_lib_ctx_get_ex_data_global(OSSL_LIB_CTX *ctx) +{ + ctx = ossl_lib_ctx_get_concrete(ctx); + if (ctx == NULL) + return NULL; + return &ctx->global; +} + +int ossl_lib_ctx_run_once(OSSL_LIB_CTX *ctx, unsigned int idx, + ossl_lib_ctx_run_once_fn run_once_fn) +{ + int done = 0, ret = 0; + + ctx = ossl_lib_ctx_get_concrete(ctx); + if (ctx == NULL) + return 0; + + if (!CRYPTO_THREAD_read_lock(ctx->oncelock)) + return 0; + done = ctx->run_once_done[idx]; + if (done) + ret = ctx->run_once_ret[idx]; + CRYPTO_THREAD_unlock(ctx->oncelock); + + if (done) + return ret; + + if (!CRYPTO_THREAD_write_lock(ctx->oncelock)) + return 0; + if (ctx->run_once_done[idx]) { + ret = ctx->run_once_ret[idx]; + CRYPTO_THREAD_unlock(ctx->oncelock); + return ret; + } + + ret = run_once_fn(ctx); + ctx->run_once_done[idx] = 1; + ctx->run_once_ret[idx] = ret; + CRYPTO_THREAD_unlock(ctx->oncelock); + + return ret; +} + +int ossl_lib_ctx_onfree(OSSL_LIB_CTX *ctx, ossl_lib_ctx_onfree_fn onfreefn) +{ + struct ossl_lib_ctx_onfree_list_st *newonfree + = OPENSSL_malloc(sizeof(*newonfree)); + + if (newonfree == NULL) + return 0; + + newonfree->fn = onfreefn; + newonfree->next = ctx->onfreelist; + ctx->onfreelist = newonfree; + + return 1; +} + +const char *ossl_lib_ctx_get_descriptor(OSSL_LIB_CTX *libctx) +{ +#ifdef FIPS_MODULE + return "FIPS internal library context"; +#else + if (ossl_lib_ctx_is_global_default(libctx)) + return "Global default library context"; + if (ossl_lib_ctx_is_default(libctx)) + return "Thread-local default library context"; + return "Non-default library context"; +#endif +} |
