1/*
2 * Copyright 2019-2022 The OpenSSL Project Authors. All Rights Reserved.
3 *
4 * Licensed under the Apache License 2.0 (the "License").  You may not use
5 * this file except in compliance with the License.  You can obtain a copy
6 * in the file LICENSE in the source distribution or at
7 * https://www.openssl.org/source/license.html
8 */
9
10#include <openssl/core.h>
11#include <openssl/core_dispatch.h>
12#include "internal/core.h"
13#include "internal/property.h"
14#include "internal/provider.h"
15
16struct algorithm_data_st {
17    OSSL_LIB_CTX *libctx;
18    int operation_id;            /* May be zero for finding them all */
19    int (*pre)(OSSL_PROVIDER *, int operation_id, int no_store, void *data,
20               int *result);
21    int (*reserve_store)(int no_store, void *data);
22    void (*fn)(OSSL_PROVIDER *, const OSSL_ALGORITHM *, int no_store,
23               void *data);
24    int (*unreserve_store)(void *data);
25    int (*post)(OSSL_PROVIDER *, int operation_id, int no_store, void *data,
26                int *result);
27    void *data;
28};
29
30/*
31 * Process one OSSL_ALGORITHM array, for the operation |cur_operation|,
32 * by constructing methods for all its implementations and adding those
33 * to the appropriate method store.
34 * Which method store is appropriate is given by |no_store| ("permanent"
35 * if 0, temporary if 1) and other data in |data->data|.
36 *
37 * Returns:
38 * -1 to quit adding algorithm implementations immediately
39 * 0 if not successful, but adding should continue
40 * 1 if successful so far, and adding should continue
41 */
42static int algorithm_do_map(OSSL_PROVIDER *provider, const OSSL_ALGORITHM *map,
43                            int cur_operation, int no_store, void *cbdata)
44{
45    struct algorithm_data_st *data = cbdata;
46    int ret = 0;
47
48    if (!data->reserve_store(no_store, data->data))
49        /* Error, bail out! */
50        return -1;
51
52    /* Do we fulfill pre-conditions? */
53    if (data->pre == NULL) {
54        /* If there is no pre-condition function, assume "yes" */
55        ret = 1;
56    } else if (!data->pre(provider, cur_operation, no_store, data->data,
57                          &ret)) {
58        /* Error, bail out! */
59        ret = -1;
60        goto end;
61    }
62
63    /*
64     * If pre-condition not fulfilled don't add this set of implementations,
65     * but do continue with the next.  This simply means that another thread
66     * got to it first.
67     */
68    if (ret == 0) {
69        ret = 1;
70        goto end;
71    }
72
73    if (map != NULL) {
74        const OSSL_ALGORITHM *thismap;
75
76        for (thismap = map; thismap->algorithm_names != NULL; thismap++)
77            data->fn(provider, thismap, no_store, data->data);
78    }
79
80    /* Do we fulfill post-conditions? */
81    if (data->post == NULL) {
82        /* If there is no post-condition function, assume "yes" */
83        ret = 1;
84    } else if (!data->post(provider, cur_operation, no_store, data->data,
85                           &ret)) {
86        /* Error, bail out! */
87        ret = -1;
88    }
89
90 end:
91    data->unreserve_store(data->data);
92
93    return ret;
94}
95
96/*
97 * Given a provider, process one operation given by |data->operation_id|, or
98 * if that's zero, process all known operations.
99 * For each such operation, query the associated OSSL_ALGORITHM array from
100 * the provider, then process that array with |algorithm_do_map()|.
101 */
102static int algorithm_do_this(OSSL_PROVIDER *provider, void *cbdata)
103{
104    struct algorithm_data_st *data = cbdata;
105    int first_operation = 1;
106    int last_operation = OSSL_OP__HIGHEST;
107    int cur_operation;
108    int ok = 1;
109
110    if (data->operation_id != 0)
111        first_operation = last_operation = data->operation_id;
112
113    for (cur_operation = first_operation;
114         cur_operation <= last_operation;
115         cur_operation++) {
116        int no_store = 0;        /* Assume caching is ok */
117        const OSSL_ALGORITHM *map = NULL;
118        int ret = 0;
119
120        map = ossl_provider_query_operation(provider, cur_operation,
121                                            &no_store);
122        ret = algorithm_do_map(provider, map, cur_operation, no_store, data);
123        ossl_provider_unquery_operation(provider, cur_operation, map);
124
125        if (ret < 0)
126            /* Hard error, bail out immediately! */
127            return 0;
128
129        /* If post-condition not fulfilled, set general failure */
130        if (!ret)
131            ok = 0;
132    }
133
134    return ok;
135}
136
137void ossl_algorithm_do_all(OSSL_LIB_CTX *libctx, int operation_id,
138                           OSSL_PROVIDER *provider,
139                           int (*pre)(OSSL_PROVIDER *, int operation_id,
140                                      int no_store, void *data, int *result),
141                           int (*reserve_store)(int no_store, void *data),
142                           void (*fn)(OSSL_PROVIDER *provider,
143                                      const OSSL_ALGORITHM *algo,
144                                      int no_store, void *data),
145                           int (*unreserve_store)(void *data),
146                           int (*post)(OSSL_PROVIDER *, int operation_id,
147                                       int no_store, void *data, int *result),
148                           void *data)
149{
150    struct algorithm_data_st cbdata = { 0, };
151
152    cbdata.libctx = libctx;
153    cbdata.operation_id = operation_id;
154    cbdata.pre = pre;
155    cbdata.reserve_store = reserve_store;
156    cbdata.fn = fn;
157    cbdata.unreserve_store = unreserve_store;
158    cbdata.post = post;
159    cbdata.data = data;
160
161    if (provider == NULL) {
162        ossl_provider_doall_activated(libctx, algorithm_do_this, &cbdata);
163    } else {
164        OSSL_LIB_CTX *libctx2 = ossl_provider_libctx(provider);
165
166        /*
167         * If a provider is given, its library context MUST match the library
168         * context we're passed.  If this turns out not to be true, there is
169         * a programming error in the functions up the call stack.
170         */
171        if (!ossl_assert(ossl_lib_ctx_get_concrete(libctx)
172                         == ossl_lib_ctx_get_concrete(libctx2)))
173            return;
174
175        cbdata.libctx = libctx2;
176        algorithm_do_this(provider, &cbdata);
177    }
178}
179
180char *ossl_algorithm_get1_first_name(const OSSL_ALGORITHM *algo)
181{
182    const char *first_name_end = NULL;
183    size_t first_name_len = 0;
184    char *ret;
185
186    if (algo->algorithm_names == NULL)
187        return NULL;
188
189    first_name_end = strchr(algo->algorithm_names, ':');
190    if (first_name_end == NULL)
191        first_name_len = strlen(algo->algorithm_names);
192    else
193        first_name_len = first_name_end - algo->algorithm_names;
194
195    ret = OPENSSL_strndup(algo->algorithm_names, first_name_len);
196    if (ret == NULL)
197        ERR_raise(ERR_LIB_EVP, ERR_R_MALLOC_FAILURE);
198    return ret;
199}
200