openssl/crypto/core_algorithm.c
Richard Levitte 10937d5867 Refactor method construction pre- and post-condition
The existing pre- and post-condition functions are supposed to check if
methods have already been created and stored, using provider operation
bits.  This is supposed to only be done for "permanent" method stores.

However, the way the pre-condition was called, it could not know if the
set of implementations to be stored is likely to end up in a "permanent"
or a temporary store.  It needs access to the |no_store| flag returned
by the provider's operation query function, because that call was done
after the pre-condition was called.

This requires a bit of refactoring, primarly of |algorithm_do_this()|,
but also of |ossl_method_construct_precondition()|.

Fixes #18150

Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/18151)
2022-05-05 15:05:54 +02:00

184 lines
6.1 KiB
C

/*
* 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 <openssl/core.h>
#include <openssl/core_dispatch.h>
#include "internal/core.h"
#include "internal/property.h"
#include "internal/provider.h"
struct algorithm_data_st {
OSSL_LIB_CTX *libctx;
int operation_id; /* May be zero for finding them all */
int (*pre)(OSSL_PROVIDER *, int operation_id, int no_store, void *data,
int *result);
void (*fn)(OSSL_PROVIDER *, const OSSL_ALGORITHM *, int no_store,
void *data);
int (*post)(OSSL_PROVIDER *, int operation_id, int no_store, void *data,
int *result);
void *data;
};
/*
* Process one OSSL_ALGORITHM array, for the operation |cur_operation|,
* by constructing methods for all its implementations and adding those
* to the appropriate method store.
* Which method store is appropriate is given by |no_store| ("permanent"
* if 0, temporary if 1) and other data in |data->data|.
*
* Returns:
* -1 to quit adding algorithm implementations immediately
* 0 if not successful, but adding should continue
* 1 if successful so far, and adding should continue
*/
static int algorithm_do_map(OSSL_PROVIDER *provider, const OSSL_ALGORITHM *map,
int cur_operation, int no_store, void *cbdata)
{
struct algorithm_data_st *data = cbdata;
int ret = 0;
/* Do we fulfill pre-conditions? */
if (data->pre == NULL) {
/* If there is no pre-condition function, assume "yes" */
ret = 1;
} else if (!data->pre(provider, cur_operation, no_store, data->data,
&ret)) {
/* Error, bail out! */
return -1;
}
/*
* If pre-condition not fulfilled don't add this set of implementations,
* but do continue with the next. This simply means that another thread
* got to it first.
*/
if (ret == 0)
return 1;
if (map != NULL) {
const OSSL_ALGORITHM *thismap;
for (thismap = map; thismap->algorithm_names != NULL; thismap++)
data->fn(provider, thismap, no_store, data->data);
}
/* Do we fulfill post-conditions? */
if (data->post == NULL) {
/* If there is no post-condition function, assume "yes" */
ret = 1;
} else if (!data->post(provider, cur_operation, no_store, data->data,
&ret)) {
/* Error, bail out! */
return -1;
}
return ret;
}
/*
* Given a provider, process one operation given by |data->operation_id|, or
* if that's zero, process all known operations.
* For each such operation, query the associated OSSL_ALGORITHM array from
* the provider, then process that array with |algorithm_do_map()|.
*/
static int algorithm_do_this(OSSL_PROVIDER *provider, void *cbdata)
{
struct algorithm_data_st *data = cbdata;
int first_operation = 1;
int last_operation = OSSL_OP__HIGHEST;
int cur_operation;
int ok = 1;
if (data->operation_id != 0)
first_operation = last_operation = data->operation_id;
for (cur_operation = first_operation;
cur_operation <= last_operation;
cur_operation++) {
int no_store = 0; /* Assume caching is ok */
const OSSL_ALGORITHM *map = NULL;
int ret;
map = ossl_provider_query_operation(provider, cur_operation,
&no_store);
ret = algorithm_do_map(provider, map, cur_operation, no_store, data);
ossl_provider_unquery_operation(provider, cur_operation, map);
if (ret < 0)
/* Hard error, bail out immediately! */
return 0;
/* If post-condition not fulfilled, set general failure */
if (!ret)
ok = 0;
}
return ok;
}
void ossl_algorithm_do_all(OSSL_LIB_CTX *libctx, int operation_id,
OSSL_PROVIDER *provider,
int (*pre)(OSSL_PROVIDER *, int operation_id,
int no_store, void *data, int *result),
void (*fn)(OSSL_PROVIDER *provider,
const OSSL_ALGORITHM *algo,
int no_store, void *data),
int (*post)(OSSL_PROVIDER *, int operation_id,
int no_store, void *data, int *result),
void *data)
{
struct algorithm_data_st cbdata = { 0, };
cbdata.libctx = libctx;
cbdata.operation_id = operation_id;
cbdata.pre = pre;
cbdata.fn = fn;
cbdata.post = post;
cbdata.data = data;
if (provider == NULL) {
ossl_provider_doall_activated(libctx, algorithm_do_this, &cbdata);
} else {
OSSL_LIB_CTX *libctx2 = ossl_provider_libctx(provider);
/*
* If a provider is given, its library context MUST match the library
* context we're passed. If this turns out not to be true, there is
* a programming error in the functions up the call stack.
*/
if (!ossl_assert(ossl_lib_ctx_get_concrete(libctx)
== ossl_lib_ctx_get_concrete(libctx2)))
return;
cbdata.libctx = libctx2;
algorithm_do_this(provider, &cbdata);
}
}
char *ossl_algorithm_get1_first_name(const OSSL_ALGORITHM *algo)
{
const char *first_name_end = NULL;
size_t first_name_len = 0;
char *ret;
if (algo->algorithm_names == NULL)
return NULL;
first_name_end = strchr(algo->algorithm_names, ':');
if (first_name_end == NULL)
first_name_len = strlen(algo->algorithm_names);
else
first_name_len = first_name_end - algo->algorithm_names;
ret = OPENSSL_strndup(algo->algorithm_names, first_name_len);
if (ret == NULL)
ERR_raise(ERR_LIB_EVP, ERR_R_MALLOC_FAILURE);
return ret;
}