1 /*
2 * Copyright 2020-2025 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 /*
11 * A filtering provider for test purposes. We pass all calls through to the
12 * default provider except where we want other behaviour for a test.
13 */
14
15 #include <string.h>
16 #include <openssl/core.h>
17 #include <openssl/core_dispatch.h>
18 #include <openssl/provider.h>
19 #include <openssl/crypto.h>
20 #include "testutil.h"
21 #include "filterprov.h"
22 #include "prov/bio.h"
23
24 #define MAX_FILTERS 10
25 #define MAX_ALG_FILTERS 5
26
27 struct filter_prov_globals_st {
28 OSSL_LIB_CTX *libctx;
29 OSSL_PROVIDER *deflt;
30 struct {
31 int operation;
32 OSSL_ALGORITHM alg[MAX_ALG_FILTERS + 1];
33 } dispatch[MAX_FILTERS];
34 int num_dispatch;
35 int no_cache;
36 unsigned long int query_count;
37 int error;
38 };
39
40 static struct filter_prov_globals_st ourglobals;
41
get_globals(void)42 static struct filter_prov_globals_st *get_globals(void)
43 {
44 /*
45 * Ideally we'd like to store this in the OSSL_LIB_CTX so that we can have
46 * more than one instance of the filter provider at a time. But for now we
47 * just make it simple.
48 */
49 return &ourglobals;
50 }
51
52 static OSSL_FUNC_provider_gettable_params_fn filter_gettable_params;
53 static OSSL_FUNC_provider_get_params_fn filter_get_params;
54 static OSSL_FUNC_provider_query_operation_fn filter_query;
55 static OSSL_FUNC_provider_unquery_operation_fn filter_unquery;
56 static OSSL_FUNC_provider_teardown_fn filter_teardown;
57
filter_gettable_params(void * provctx)58 static const OSSL_PARAM *filter_gettable_params(void *provctx)
59 {
60 struct filter_prov_globals_st *globs = get_globals();
61
62 return OSSL_PROVIDER_gettable_params(globs->deflt);
63 }
64
filter_get_params(void * provctx,OSSL_PARAM params[])65 static int filter_get_params(void *provctx, OSSL_PARAM params[])
66 {
67 struct filter_prov_globals_st *globs = get_globals();
68
69 return OSSL_PROVIDER_get_params(globs->deflt, params);
70 }
71
filter_get_capabilities(void * provctx,const char * capability,OSSL_CALLBACK * cb,void * arg)72 static int filter_get_capabilities(void *provctx, const char *capability,
73 OSSL_CALLBACK *cb, void *arg)
74 {
75 struct filter_prov_globals_st *globs = get_globals();
76
77 return OSSL_PROVIDER_get_capabilities(globs->deflt, capability, cb, arg);
78 }
79
filter_query(void * provctx,int operation_id,int * no_cache)80 static const OSSL_ALGORITHM *filter_query(void *provctx,
81 int operation_id,
82 int *no_cache)
83 {
84 struct filter_prov_globals_st *globs = get_globals();
85 int i;
86
87 globs->query_count++;
88 for (i = 0; i < globs->num_dispatch; i++) {
89 if (globs->dispatch[i].operation == operation_id) {
90 *no_cache = globs->no_cache;
91 return globs->dispatch[i].alg;
92 }
93 }
94
95 /* No filter set, so pass it down to the chained provider */
96 return OSSL_PROVIDER_query_operation(globs->deflt, operation_id, no_cache);
97 }
98
filter_unquery(void * provctx,int operation_id,const OSSL_ALGORITHM * algs)99 static void filter_unquery(void *provctx, int operation_id,
100 const OSSL_ALGORITHM *algs)
101 {
102 struct filter_prov_globals_st *globs = get_globals();
103 int i;
104
105 if (!TEST_ulong_gt(globs->query_count, 0))
106 globs->error = 1;
107 else
108 globs->query_count--;
109
110 for (i = 0; i < globs->num_dispatch; i++)
111 if (globs->dispatch[i].alg == algs)
112 return;
113 OSSL_PROVIDER_unquery_operation(globs->deflt, operation_id, algs);
114 }
115
filter_teardown(void * provctx)116 static void filter_teardown(void *provctx)
117 {
118 struct filter_prov_globals_st *globs = get_globals();
119
120 OSSL_PROVIDER_unload(globs->deflt);
121 OSSL_LIB_CTX_free(globs->libctx);
122 memset(globs, 0, sizeof(*globs));
123 BIO_meth_free(ossl_prov_ctx_get0_core_bio_method(provctx));
124 ossl_prov_ctx_free(provctx);
125 }
126
127 /* Functions we provide to the core */
128 static const OSSL_DISPATCH filter_dispatch_table[] = {
129 { OSSL_FUNC_PROVIDER_GETTABLE_PARAMS, (void (*)(void))filter_gettable_params },
130 { OSSL_FUNC_PROVIDER_GET_PARAMS, (void (*)(void))filter_get_params },
131 { OSSL_FUNC_PROVIDER_QUERY_OPERATION, (void (*)(void))filter_query },
132 { OSSL_FUNC_PROVIDER_UNQUERY_OPERATION, (void (*)(void))filter_unquery },
133 { OSSL_FUNC_PROVIDER_GET_CAPABILITIES, (void (*)(void))filter_get_capabilities },
134 { OSSL_FUNC_PROVIDER_TEARDOWN, (void (*)(void))filter_teardown },
135 OSSL_DISPATCH_END
136 };
137
filter_provider_init(const OSSL_CORE_HANDLE * handle,const OSSL_DISPATCH * in,const OSSL_DISPATCH ** out,void ** provctx)138 int filter_provider_init(const OSSL_CORE_HANDLE *handle,
139 const OSSL_DISPATCH *in,
140 const OSSL_DISPATCH **out,
141 void **provctx)
142 {
143 OSSL_FUNC_core_get_libctx_fn *c_get_libctx = NULL;
144 BIO_METHOD *corebiometh;
145
146 if (!ossl_prov_bio_from_dispatch(in))
147 return 0;
148 for (; in->function_id != 0; in++) {
149 switch (in->function_id) {
150 case OSSL_FUNC_CORE_GET_LIBCTX:
151 c_get_libctx = OSSL_FUNC_core_get_libctx(in);
152 break;
153 default:
154 /* Just ignore anything we don't understand */
155 break;
156 }
157 }
158
159 if (c_get_libctx == NULL)
160 return 0;
161
162 memset(&ourglobals, 0, sizeof(ourglobals));
163 ourglobals.libctx = OSSL_LIB_CTX_new();
164 if (ourglobals.libctx == NULL)
165 goto err;
166
167 ourglobals.deflt = OSSL_PROVIDER_load(ourglobals.libctx, "default");
168 if (ourglobals.deflt == NULL)
169 goto err;
170
171 /*
172 * We want to make sure that all calls from this provider that requires
173 * a library context use the same context as the one used to call our
174 * functions. We do that by passing it along in the provider context.
175 *
176 * This only works for built-in providers. Most providers should
177 * create their own library context.
178 */
179 if ((*provctx = ossl_prov_ctx_new()) == NULL
180 || (corebiometh = ossl_bio_prov_init_bio_method()) == NULL) {
181 ossl_prov_ctx_free(*provctx);
182 *provctx = NULL;
183 goto err;
184 }
185 ossl_prov_ctx_set0_libctx(*provctx, (OSSL_LIB_CTX *)c_get_libctx(handle));
186 ossl_prov_ctx_set0_handle(*provctx, handle);
187 ossl_prov_ctx_set0_core_bio_method(*provctx, corebiometh);
188 *out = filter_dispatch_table;
189 return 1;
190
191 err:
192 OSSL_PROVIDER_unload(ourglobals.deflt);
193 OSSL_LIB_CTX_free(ourglobals.libctx);
194 return 0;
195 }
196
197 /*
198 * Set a filter for the given operation id. The filter string is a colon
199 * separated list of algorithms that will be made available by this provider.
200 * Anything not in the filter will be suppressed. If a filter is not set for
201 * a given operation id then all algorithms are made available.
202 */
filter_provider_set_filter(int operation,const char * filterstr)203 int filter_provider_set_filter(int operation, const char *filterstr)
204 {
205 int no_cache = 0;
206 int algnum = 0, last = 0, ret = 0;
207 struct filter_prov_globals_st *globs = get_globals();
208 size_t namelen;
209 char *filterstrtmp = OPENSSL_strdup(filterstr);
210 char *name, *sep;
211 const OSSL_ALGORITHM *provalgs = OSSL_PROVIDER_query_operation(globs->deflt,
212 operation,
213 &no_cache);
214 const OSSL_ALGORITHM *algs;
215
216 if (filterstrtmp == NULL)
217 goto err;
218
219 /* Nothing to filter */
220 if (provalgs == NULL)
221 goto err;
222
223 if (globs->num_dispatch >= MAX_FILTERS)
224 goto err;
225
226 for (name = filterstrtmp; !last; name = (sep == NULL ? NULL : sep + 1)) {
227 sep = strstr(name, ":");
228 if (sep != NULL)
229 *sep = '\0';
230 else
231 last = 1;
232 namelen = strlen(name);
233
234 for (algs = provalgs; algs->algorithm_names != NULL; algs++) {
235 const char *found = strstr(algs->algorithm_names, name);
236
237 if (found == NULL)
238 continue;
239 if (found[namelen] != '\0' && found[namelen] != ':')
240 continue;
241 if (found != algs->algorithm_names && found[-1] != ':')
242 continue;
243
244 /* We found a match */
245 if (algnum >= MAX_ALG_FILTERS)
246 goto err;
247
248 globs->dispatch[globs->num_dispatch].alg[algnum++] = *algs;
249 break;
250 }
251 if (algs->algorithm_names == NULL) {
252 /* No match found */
253 goto err;
254 }
255 }
256
257 globs->dispatch[globs->num_dispatch].operation = operation;
258 globs->no_cache = no_cache;
259 globs->num_dispatch++;
260
261 ret = 1;
262 err:
263 OSSL_PROVIDER_unquery_operation(globs->deflt, operation, provalgs);
264 OPENSSL_free(filterstrtmp);
265 return ret;
266 }
267
268 /*
269 * Test if a filter provider is in a clean finishing state.
270 * If it is return 1, otherwise return 0.
271 */
filter_provider_check_clean_finish(void)272 int filter_provider_check_clean_finish(void)
273 {
274 struct filter_prov_globals_st *globs = get_globals();
275
276 return TEST_ulong_eq(globs->query_count, 0) && !globs->error;
277 }
278