| /* |
| * Copyright 2020-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 <openssl/crypto.h> |
| #include "crypto/store.h" |
| #include "internal/core.h" |
| #include "internal/namemap.h" |
| #include "internal/property.h" |
| #include "internal/provider.h" |
| #include "store_local.h" |
| |
| int OSSL_STORE_LOADER_up_ref(OSSL_STORE_LOADER *loader) |
| { |
| int ref = 0; |
| |
| if (loader->prov != NULL) |
| CRYPTO_UP_REF(&loader->refcnt, &ref, loader->lock); |
| return 1; |
| } |
| |
| void OSSL_STORE_LOADER_free(OSSL_STORE_LOADER *loader) |
| { |
| if (loader != NULL && loader->prov != NULL) { |
| int i; |
| |
| CRYPTO_DOWN_REF(&loader->refcnt, &i, loader->lock); |
| if (i > 0) |
| return; |
| ossl_provider_free(loader->prov); |
| CRYPTO_THREAD_lock_free(loader->lock); |
| } |
| OPENSSL_free(loader); |
| } |
| |
| /* |
| * OSSL_STORE_LOADER_new() expects the scheme as a constant string, |
| * which we currently don't have, so we need an alternative allocator. |
| */ |
| static OSSL_STORE_LOADER *new_loader(OSSL_PROVIDER *prov) |
| { |
| OSSL_STORE_LOADER *loader; |
| |
| if ((loader = OPENSSL_zalloc(sizeof(*loader))) == NULL |
| || (loader->lock = CRYPTO_THREAD_lock_new()) == NULL) { |
| OPENSSL_free(loader); |
| return NULL; |
| } |
| loader->prov = prov; |
| ossl_provider_up_ref(prov); |
| loader->refcnt = 1; |
| |
| return loader; |
| } |
| |
| static int up_ref_loader(void *method) |
| { |
| return OSSL_STORE_LOADER_up_ref(method); |
| } |
| |
| static void free_loader(void *method) |
| { |
| OSSL_STORE_LOADER_free(method); |
| } |
| |
| /* Permanent loader method store, constructor and destructor */ |
| static void loader_store_free(void *vstore) |
| { |
| ossl_method_store_free(vstore); |
| } |
| |
| static void *loader_store_new(OSSL_LIB_CTX *ctx) |
| { |
| return ossl_method_store_new(ctx); |
| } |
| |
| |
| static const OSSL_LIB_CTX_METHOD loader_store_method = { |
| /* We want loader_store to be cleaned up before the provider store */ |
| OSSL_LIB_CTX_METHOD_PRIORITY_2, |
| loader_store_new, |
| loader_store_free, |
| }; |
| |
| /* Data to be passed through ossl_method_construct() */ |
| struct loader_data_st { |
| OSSL_LIB_CTX *libctx; |
| int scheme_id; /* For get_loader_from_store() */ |
| const char *scheme; /* For get_loader_from_store() */ |
| const char *propquery; /* For get_loader_from_store() */ |
| |
| OSSL_METHOD_STORE *tmp_store; /* For get_tmp_loader_store() */ |
| |
| unsigned int flag_construct_error_occurred : 1; |
| }; |
| |
| /* |
| * Generic routines to fetch / create OSSL_STORE methods with |
| * ossl_method_construct() |
| */ |
| |
| /* Temporary loader method store, constructor and destructor */ |
| static void *get_tmp_loader_store(void *data) |
| { |
| struct loader_data_st *methdata = data; |
| |
| if (methdata->tmp_store == NULL) |
| methdata->tmp_store = ossl_method_store_new(methdata->libctx); |
| return methdata->tmp_store; |
| } |
| |
| static void dealloc_tmp_loader_store(void *store) |
| { |
| if (store != NULL) |
| ossl_method_store_free(store); |
| } |
| |
| /* Get the permanent loader store */ |
| static OSSL_METHOD_STORE *get_loader_store(OSSL_LIB_CTX *libctx) |
| { |
| return ossl_lib_ctx_get_data(libctx, OSSL_LIB_CTX_STORE_LOADER_STORE_INDEX, |
| &loader_store_method); |
| } |
| |
| /* Get loader methods from a store, or put one in */ |
| static void *get_loader_from_store(void *store, const OSSL_PROVIDER **prov, |
| void *data) |
| { |
| struct loader_data_st *methdata = data; |
| void *method = NULL; |
| int id; |
| |
| if ((id = methdata->scheme_id) == 0) { |
| OSSL_NAMEMAP *namemap = ossl_namemap_stored(methdata->libctx); |
| |
| id = ossl_namemap_name2num(namemap, methdata->scheme); |
| } |
| |
| if (store == NULL |
| && (store = get_loader_store(methdata->libctx)) == NULL) |
| return NULL; |
| |
| if (!ossl_method_store_fetch(store, id, methdata->propquery, prov, &method)) |
| return NULL; |
| return method; |
| } |
| |
| static int put_loader_in_store(void *store, void *method, |
| const OSSL_PROVIDER *prov, |
| const char *scheme, const char *propdef, |
| void *data) |
| { |
| struct loader_data_st *methdata = data; |
| OSSL_NAMEMAP *namemap; |
| int id; |
| |
| if ((namemap = ossl_namemap_stored(methdata->libctx)) == NULL |
| || (id = ossl_namemap_name2num(namemap, scheme)) == 0) |
| return 0; |
| |
| if (store == NULL && (store = get_loader_store(methdata->libctx)) == NULL) |
| return 0; |
| |
| return ossl_method_store_add(store, prov, id, propdef, method, |
| up_ref_loader, free_loader); |
| } |
| |
| static void *loader_from_algorithm(int scheme_id, const OSSL_ALGORITHM *algodef, |
| OSSL_PROVIDER *prov) |
| { |
| OSSL_STORE_LOADER *loader = NULL; |
| const OSSL_DISPATCH *fns = algodef->implementation; |
| |
| if ((loader = new_loader(prov)) == NULL) |
| return NULL; |
| loader->scheme_id = scheme_id; |
| loader->propdef = algodef->property_definition; |
| loader->description = algodef->algorithm_description; |
| |
| for (; fns->function_id != 0; fns++) { |
| switch (fns->function_id) { |
| case OSSL_FUNC_STORE_OPEN: |
| if (loader->p_open == NULL) |
| loader->p_open = OSSL_FUNC_store_open(fns); |
| break; |
| case OSSL_FUNC_STORE_ATTACH: |
| if (loader->p_attach == NULL) |
| loader->p_attach = OSSL_FUNC_store_attach(fns); |
| break; |
| case OSSL_FUNC_STORE_SETTABLE_CTX_PARAMS: |
| if (loader->p_settable_ctx_params == NULL) |
| loader->p_settable_ctx_params = |
| OSSL_FUNC_store_settable_ctx_params(fns); |
| break; |
| case OSSL_FUNC_STORE_SET_CTX_PARAMS: |
| if (loader->p_set_ctx_params == NULL) |
| loader->p_set_ctx_params = OSSL_FUNC_store_set_ctx_params(fns); |
| break; |
| case OSSL_FUNC_STORE_LOAD: |
| if (loader->p_load == NULL) |
| loader->p_load = OSSL_FUNC_store_load(fns); |
| break; |
| case OSSL_FUNC_STORE_EOF: |
| if (loader->p_eof == NULL) |
| loader->p_eof = OSSL_FUNC_store_eof(fns); |
| break; |
| case OSSL_FUNC_STORE_CLOSE: |
| if (loader->p_close == NULL) |
| loader->p_close = OSSL_FUNC_store_close(fns); |
| break; |
| case OSSL_FUNC_STORE_EXPORT_OBJECT: |
| if (loader->p_export_object == NULL) |
| loader->p_export_object = OSSL_FUNC_store_export_object(fns); |
| break; |
| } |
| } |
| |
| if ((loader->p_open == NULL && loader->p_attach == NULL) |
| || loader->p_load == NULL |
| || loader->p_eof == NULL |
| || loader->p_close == NULL) { |
| /* Only set_ctx_params is optionaal */ |
| OSSL_STORE_LOADER_free(loader); |
| ERR_raise(ERR_LIB_OSSL_STORE, OSSL_STORE_R_LOADER_INCOMPLETE); |
| return NULL; |
| } |
| return loader; |
| } |
| |
| /* |
| * The core fetching functionality passes the scheme of the implementation. |
| * This function is responsible to getting an identity number for them, |
| * then call loader_from_algorithm() with that identity number. |
| */ |
| static void *construct_loader(const OSSL_ALGORITHM *algodef, |
| OSSL_PROVIDER *prov, void *data) |
| { |
| /* |
| * This function is only called if get_loader_from_store() returned |
| * NULL, so it's safe to say that of all the spots to create a new |
| * namemap entry, this is it. Should the scheme already exist there, we |
| * know that ossl_namemap_add() will return its corresponding number. |
| */ |
| struct loader_data_st *methdata = data; |
| OSSL_LIB_CTX *libctx = ossl_provider_libctx(prov); |
| OSSL_NAMEMAP *namemap = ossl_namemap_stored(libctx); |
| const char *scheme = algodef->algorithm_names; |
| int id = ossl_namemap_add_name(namemap, 0, scheme); |
| void *method = NULL; |
| |
| if (id != 0) |
| method = loader_from_algorithm(id, algodef, prov); |
| |
| /* |
| * Flag to indicate that there was actual construction errors. This |
| * helps inner_loader_fetch() determine what error it should |
| * record on inaccessible algorithms. |
| */ |
| if (method == NULL) |
| methdata->flag_construct_error_occurred = 1; |
| |
| return method; |
| } |
| |
| /* Intermediary function to avoid ugly casts, used below */ |
| static void destruct_loader(void *method, void *data) |
| { |
| OSSL_STORE_LOADER_free(method); |
| } |
| |
| /* Fetching support. Can fetch by numeric identity or by scheme */ |
| static OSSL_STORE_LOADER * |
| inner_loader_fetch(struct loader_data_st *methdata, int id, |
| const char *scheme, const char *properties) |
| { |
| OSSL_METHOD_STORE *store = get_loader_store(methdata->libctx); |
| OSSL_NAMEMAP *namemap = ossl_namemap_stored(methdata->libctx); |
| const char *const propq = properties != NULL ? properties : ""; |
| void *method = NULL; |
| int unsupported = 0; |
| |
| if (store == NULL || namemap == NULL) { |
| ERR_raise(ERR_LIB_OSSL_STORE, ERR_R_PASSED_INVALID_ARGUMENT); |
| return NULL; |
| } |
| |
| /* |
| * If we have been passed both an id and a scheme, we have an |
| * internal programming error. |
| */ |
| if (!ossl_assert(id == 0 || scheme == NULL)) { |
| ERR_raise(ERR_LIB_OSSL_STORE, ERR_R_INTERNAL_ERROR); |
| return NULL; |
| } |
| |
| /* If we haven't received a name id yet, try to get one for the name */ |
| if (id == 0 && scheme != NULL) |
| id = ossl_namemap_name2num(namemap, scheme); |
| |
| /* |
| * If we haven't found the name yet, chances are that the algorithm to |
| * be fetched is unsupported. |
| */ |
| if (id == 0) |
| unsupported = 1; |
| |
| if (id == 0 |
| || !ossl_method_store_cache_get(store, NULL, id, propq, &method)) { |
| OSSL_METHOD_CONSTRUCT_METHOD mcm = { |
| get_tmp_loader_store, |
| get_loader_from_store, |
| put_loader_in_store, |
| construct_loader, |
| destruct_loader |
| }; |
| OSSL_PROVIDER *prov = NULL; |
| |
| methdata->scheme_id = id; |
| methdata->scheme = scheme; |
| methdata->propquery = propq; |
| methdata->flag_construct_error_occurred = 0; |
| if ((method = ossl_method_construct(methdata->libctx, OSSL_OP_STORE, |
| &prov, 0 /* !force_cache */, |
| &mcm, methdata)) != NULL) { |
| /* |
| * If construction did create a method for us, we know that there |
| * is a correct scheme_id, since those have already been calculated |
| * in get_loader_from_store() and put_loader_in_store() above. |
| */ |
| if (id == 0) |
| id = ossl_namemap_name2num(namemap, scheme); |
| ossl_method_store_cache_set(store, prov, id, propq, method, |
| up_ref_loader, free_loader); |
| } |
| |
| /* |
| * If we never were in the constructor, the algorithm to be fetched |
| * is unsupported. |
| */ |
| unsupported = !methdata->flag_construct_error_occurred; |
| } |
| |
| if ((id != 0 || scheme != NULL) && method == NULL) { |
| int code = unsupported ? ERR_R_UNSUPPORTED : ERR_R_FETCH_FAILED; |
| const char *helpful_msg = |
| unsupported |
| ? ( "No store loader found. For standard store loaders you need " |
| "at least one of the default or base providers available. " |
| "Did you forget to load them? Info: " ) |
| : ""; |
| |
| if (scheme == NULL) |
| scheme = ossl_namemap_num2name(namemap, id, 0); |
| ERR_raise_data(ERR_LIB_OSSL_STORE, code, |
| "%s%s, Scheme (%s : %d), Properties (%s)", |
| helpful_msg, |
| ossl_lib_ctx_get_descriptor(methdata->libctx), |
| scheme = NULL ? "<null>" : scheme, id, |
| properties == NULL ? "<null>" : properties); |
| } |
| |
| return method; |
| } |
| |
| OSSL_STORE_LOADER *OSSL_STORE_LOADER_fetch(OSSL_LIB_CTX *libctx, |
| const char *scheme, |
| const char *properties) |
| { |
| struct loader_data_st methdata; |
| void *method; |
| |
| methdata.libctx = libctx; |
| methdata.tmp_store = NULL; |
| method = inner_loader_fetch(&methdata, 0, scheme, properties); |
| dealloc_tmp_loader_store(methdata.tmp_store); |
| return method; |
| } |
| |
| OSSL_STORE_LOADER *ossl_store_loader_fetch_by_number(OSSL_LIB_CTX *libctx, |
| int scheme_id, |
| const char *properties) |
| { |
| struct loader_data_st methdata; |
| void *method; |
| |
| methdata.libctx = libctx; |
| methdata.tmp_store = NULL; |
| method = inner_loader_fetch(&methdata, scheme_id, NULL, properties); |
| dealloc_tmp_loader_store(methdata.tmp_store); |
| return method; |
| } |
| |
| /* |
| * Library of basic method functions |
| */ |
| |
| const OSSL_PROVIDER *OSSL_STORE_LOADER_get0_provider(const OSSL_STORE_LOADER *loader) |
| { |
| if (!ossl_assert(loader != NULL)) { |
| ERR_raise(ERR_LIB_OSSL_STORE, ERR_R_PASSED_NULL_PARAMETER); |
| return 0; |
| } |
| |
| return loader->prov; |
| } |
| |
| const char *OSSL_STORE_LOADER_get0_properties(const OSSL_STORE_LOADER *loader) |
| { |
| if (!ossl_assert(loader != NULL)) { |
| ERR_raise(ERR_LIB_OSSL_STORE, ERR_R_PASSED_NULL_PARAMETER); |
| return 0; |
| } |
| |
| return loader->propdef; |
| } |
| |
| int ossl_store_loader_get_number(const OSSL_STORE_LOADER *loader) |
| { |
| if (!ossl_assert(loader != NULL)) { |
| ERR_raise(ERR_LIB_OSSL_STORE, ERR_R_PASSED_NULL_PARAMETER); |
| return 0; |
| } |
| |
| return loader->scheme_id; |
| } |
| |
| const char *OSSL_STORE_LOADER_get0_description(const OSSL_STORE_LOADER *loader) |
| { |
| return loader->description; |
| } |
| |
| int OSSL_STORE_LOADER_is_a(const OSSL_STORE_LOADER *loader, const char *name) |
| { |
| if (loader->prov != NULL) { |
| OSSL_LIB_CTX *libctx = ossl_provider_libctx(loader->prov); |
| OSSL_NAMEMAP *namemap = ossl_namemap_stored(libctx); |
| |
| return ossl_namemap_name2num(namemap, name) == loader->scheme_id; |
| } |
| return 0; |
| } |
| |
| struct do_one_data_st { |
| void (*user_fn)(OSSL_STORE_LOADER *loader, void *arg); |
| void *user_arg; |
| }; |
| |
| static void do_one(ossl_unused int id, void *method, void *arg) |
| { |
| struct do_one_data_st *data = arg; |
| |
| data->user_fn(method, data->user_arg); |
| } |
| |
| void OSSL_STORE_LOADER_do_all_provided(OSSL_LIB_CTX *libctx, |
| void (*user_fn)(OSSL_STORE_LOADER *loader, |
| void *arg), |
| void *user_arg) |
| { |
| struct loader_data_st methdata; |
| struct do_one_data_st data; |
| |
| methdata.libctx = libctx; |
| methdata.tmp_store = NULL; |
| (void)inner_loader_fetch(&methdata, 0, NULL, NULL /* properties */); |
| |
| data.user_fn = user_fn; |
| data.user_arg = user_arg; |
| if (methdata.tmp_store != NULL) |
| ossl_method_store_do_all(methdata.tmp_store, &do_one, &data); |
| ossl_method_store_do_all(get_loader_store(libctx), &do_one, &data); |
| dealloc_tmp_loader_store(methdata.tmp_store); |
| } |
| |
| int OSSL_STORE_LOADER_names_do_all(const OSSL_STORE_LOADER *loader, |
| void (*fn)(const char *name, void *data), |
| void *data) |
| { |
| if (loader == NULL) |
| return 0; |
| |
| if (loader->prov != NULL) { |
| OSSL_LIB_CTX *libctx = ossl_provider_libctx(loader->prov); |
| OSSL_NAMEMAP *namemap = ossl_namemap_stored(libctx); |
| |
| return ossl_namemap_doall_names(namemap, loader->scheme_id, fn, data); |
| } |
| |
| return 1; |
| } |