| /* |
| * Copyright 2019-2021 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" |
| |
| 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; |
| } |
| #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 acquire 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 |
| } |