1/* 2 * Copyright 2020-2021 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/provider.h> 18#include <openssl/crypto.h> 19#include "testutil.h" 20#include "filterprov.h" 21 22#define MAX_FILTERS 10 23#define MAX_ALG_FILTERS 5 24 25struct filter_prov_globals_st { 26 OSSL_LIB_CTX *libctx; 27 OSSL_PROVIDER *deflt; 28 struct { 29 int operation; 30 OSSL_ALGORITHM alg[MAX_ALG_FILTERS + 1]; 31 } dispatch[MAX_FILTERS]; 32 int num_dispatch; 33 int no_cache; 34 unsigned long int query_count; 35 int error; 36}; 37 38static struct filter_prov_globals_st ourglobals; 39 40static struct filter_prov_globals_st *get_globals(void) 41{ 42 /* 43 * Ideally we'd like to store this in the OSSL_LIB_CTX so that we can have 44 * more than one instance of the filter provider at a time. But for now we 45 * just make it simple. 46 */ 47 return &ourglobals; 48} 49 50static OSSL_FUNC_provider_gettable_params_fn filter_gettable_params; 51static OSSL_FUNC_provider_get_params_fn filter_get_params; 52static OSSL_FUNC_provider_query_operation_fn filter_query; 53static OSSL_FUNC_provider_unquery_operation_fn filter_unquery; 54static OSSL_FUNC_provider_teardown_fn filter_teardown; 55 56static const OSSL_PARAM *filter_gettable_params(void *provctx) 57{ 58 struct filter_prov_globals_st *globs = get_globals(); 59 60 return OSSL_PROVIDER_gettable_params(globs->deflt); 61} 62 63static int filter_get_params(void *provctx, OSSL_PARAM params[]) 64{ 65 struct filter_prov_globals_st *globs = get_globals(); 66 67 return OSSL_PROVIDER_get_params(globs->deflt, params); 68} 69 70static int filter_get_capabilities(void *provctx, const char *capability, 71 OSSL_CALLBACK *cb, void *arg) 72{ 73 struct filter_prov_globals_st *globs = get_globals(); 74 75 return OSSL_PROVIDER_get_capabilities(globs->deflt, capability, cb, arg); 76} 77 78static const OSSL_ALGORITHM *filter_query(void *provctx, 79 int operation_id, 80 int *no_cache) 81{ 82 struct filter_prov_globals_st *globs = get_globals(); 83 int i; 84 85 globs->query_count++; 86 for (i = 0; i < globs->num_dispatch; i++) { 87 if (globs->dispatch[i].operation == operation_id) { 88 *no_cache = globs->no_cache; 89 return globs->dispatch[i].alg; 90 } 91 } 92 93 /* No filter set, so pass it down to the chained provider */ 94 return OSSL_PROVIDER_query_operation(globs->deflt, operation_id, no_cache); 95} 96 97static void filter_unquery(void *provctx, int operation_id, 98 const OSSL_ALGORITHM *algs) 99{ 100 struct filter_prov_globals_st *globs = get_globals(); 101 int i; 102 103 if (!TEST_ulong_gt(globs->query_count, 0)) 104 globs->error = 1; 105 else 106 globs->query_count--; 107 108 for (i = 0; i < globs->num_dispatch; i++) 109 if (globs->dispatch[i].alg == algs) 110 return; 111 OSSL_PROVIDER_unquery_operation(globs->deflt, operation_id, algs); 112} 113 114static void filter_teardown(void *provctx) 115{ 116 struct filter_prov_globals_st *globs = get_globals(); 117 118 OSSL_PROVIDER_unload(globs->deflt); 119 OSSL_LIB_CTX_free(globs->libctx); 120 memset(globs, 0, sizeof(*globs)); 121} 122 123/* Functions we provide to the core */ 124static const OSSL_DISPATCH filter_dispatch_table[] = { 125 { OSSL_FUNC_PROVIDER_GETTABLE_PARAMS, (void (*)(void))filter_gettable_params }, 126 { OSSL_FUNC_PROVIDER_GET_PARAMS, (void (*)(void))filter_get_params }, 127 { OSSL_FUNC_PROVIDER_QUERY_OPERATION, (void (*)(void))filter_query }, 128 { OSSL_FUNC_PROVIDER_UNQUERY_OPERATION, (void (*)(void))filter_unquery }, 129 { OSSL_FUNC_PROVIDER_GET_CAPABILITIES, (void (*)(void))filter_get_capabilities }, 130 { OSSL_FUNC_PROVIDER_TEARDOWN, (void (*)(void))filter_teardown }, 131 { 0, NULL } 132}; 133 134int filter_provider_init(const OSSL_CORE_HANDLE *handle, 135 const OSSL_DISPATCH *in, 136 const OSSL_DISPATCH **out, 137 void **provctx) 138{ 139 memset(&ourglobals, 0, sizeof(ourglobals)); 140 ourglobals.libctx = OSSL_LIB_CTX_new(); 141 if (ourglobals.libctx == NULL) 142 goto err; 143 144 ourglobals.deflt = OSSL_PROVIDER_load(ourglobals.libctx, "default"); 145 if (ourglobals.deflt == NULL) 146 goto err; 147 148 *provctx = OSSL_PROVIDER_get0_provider_ctx(ourglobals.deflt); 149 *out = filter_dispatch_table; 150 return 1; 151 152 err: 153 OSSL_PROVIDER_unload(ourglobals.deflt); 154 OSSL_LIB_CTX_free(ourglobals.libctx); 155 return 0; 156} 157 158/* 159 * Set a filter for the given operation id. The filter string is a colon 160 * separated list of algorithms that will be made available by this provider. 161 * Anything not in the filter will be suppressed. If a filter is not set for 162 * a given operation id then all algorithms are made available. 163 */ 164int filter_provider_set_filter(int operation, const char *filterstr) 165{ 166 int no_cache = 0; 167 int algnum = 0, last = 0, ret = 0; 168 struct filter_prov_globals_st *globs = get_globals(); 169 size_t namelen; 170 char *filterstrtmp = OPENSSL_strdup(filterstr); 171 char *name, *sep; 172 const OSSL_ALGORITHM *provalgs = OSSL_PROVIDER_query_operation(globs->deflt, 173 operation, 174 &no_cache); 175 const OSSL_ALGORITHM *algs; 176 177 if (filterstrtmp == NULL) 178 goto err; 179 180 /* Nothing to filter */ 181 if (provalgs == NULL) 182 goto err; 183 184 if (globs->num_dispatch >= MAX_FILTERS) 185 goto err; 186 187 for (name = filterstrtmp; !last; name = (sep == NULL ? NULL : sep + 1)) { 188 sep = strstr(name, ":"); 189 if (sep != NULL) 190 *sep = '\0'; 191 else 192 last = 1; 193 namelen = strlen(name); 194 195 for (algs = provalgs; algs->algorithm_names != NULL; algs++) { 196 const char *found = strstr(algs->algorithm_names, name); 197 198 if (found == NULL) 199 continue; 200 if (found[namelen] != '\0' && found[namelen] != ':') 201 continue; 202 if (found != algs->algorithm_names && found[-1] != ':') 203 continue; 204 205 /* We found a match */ 206 if (algnum >= MAX_ALG_FILTERS) 207 goto err; 208 209 globs->dispatch[globs->num_dispatch].alg[algnum++] = *algs; 210 break; 211 } 212 if (algs->algorithm_names == NULL) { 213 /* No match found */ 214 goto err; 215 } 216 } 217 218 globs->dispatch[globs->num_dispatch].operation = operation; 219 globs->no_cache = no_cache; 220 globs->num_dispatch++; 221 222 ret = 1; 223 err: 224 OSSL_PROVIDER_unquery_operation(globs->deflt, operation, provalgs); 225 OPENSSL_free(filterstrtmp); 226 return ret; 227} 228 229/* 230 * Test if a filter provider is in a clean finishing state. 231 * If it is return 1, otherwise return 0. 232 */ 233int filter_provider_check_clean_finish(void) 234{ 235 struct filter_prov_globals_st *globs = get_globals(); 236 237 return TEST_ulong_eq(globs->query_count, 0) && !globs->error; 238} 239