xref: /freebsd/crypto/openssl/test/filterprov.c (revision e7be843b4a162e68651d3911f0357ed464915629)
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