1 | /*
|
---|
2 | * Copyright 2019-2022 The OpenSSL Project Authors. All Rights Reserved.
|
---|
3 | * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
---|
4 | *
|
---|
5 | * Licensed under the Apache License 2.0 (the "License"). You may not use
|
---|
6 | * this file except in compliance with the License. You can obtain a copy
|
---|
7 | * in the file LICENSE in the source distribution or at
|
---|
8 | * https://www.openssl.org/source/license.html
|
---|
9 | */
|
---|
10 |
|
---|
11 | #include <string.h>
|
---|
12 | #include <stdio.h>
|
---|
13 | #include <stdarg.h>
|
---|
14 | #include <openssl/crypto.h>
|
---|
15 | #include "internal/core.h"
|
---|
16 | #include "internal/property.h"
|
---|
17 | #include "internal/provider.h"
|
---|
18 | #include "internal/tsan_assist.h"
|
---|
19 | #include "crypto/ctype.h"
|
---|
20 | #include <openssl/lhash.h>
|
---|
21 | #include <openssl/rand.h>
|
---|
22 | #include "internal/thread_once.h"
|
---|
23 | #include "crypto/lhash.h"
|
---|
24 | #include "crypto/sparse_array.h"
|
---|
25 | #include "property_local.h"
|
---|
26 |
|
---|
27 | /*
|
---|
28 | * The number of elements in the query cache before we initiate a flush.
|
---|
29 | * If reducing this, also ensure the stochastic test in test/property_test.c
|
---|
30 | * isn't likely to fail.
|
---|
31 | */
|
---|
32 | #define IMPL_CACHE_FLUSH_THRESHOLD 500
|
---|
33 |
|
---|
34 | typedef struct {
|
---|
35 | void *method;
|
---|
36 | int (*up_ref)(void *);
|
---|
37 | void (*free)(void *);
|
---|
38 | } METHOD;
|
---|
39 |
|
---|
40 | typedef struct {
|
---|
41 | const OSSL_PROVIDER *provider;
|
---|
42 | OSSL_PROPERTY_LIST *properties;
|
---|
43 | METHOD method;
|
---|
44 | } IMPLEMENTATION;
|
---|
45 |
|
---|
46 | DEFINE_STACK_OF(IMPLEMENTATION)
|
---|
47 |
|
---|
48 | typedef struct {
|
---|
49 | const OSSL_PROVIDER *provider;
|
---|
50 | const char *query;
|
---|
51 | METHOD method;
|
---|
52 | char body[1];
|
---|
53 | } QUERY;
|
---|
54 |
|
---|
55 | DEFINE_LHASH_OF(QUERY);
|
---|
56 |
|
---|
57 | typedef struct {
|
---|
58 | int nid;
|
---|
59 | STACK_OF(IMPLEMENTATION) *impls;
|
---|
60 | LHASH_OF(QUERY) *cache;
|
---|
61 | } ALGORITHM;
|
---|
62 |
|
---|
63 | struct ossl_method_store_st {
|
---|
64 | OSSL_LIB_CTX *ctx;
|
---|
65 | SPARSE_ARRAY_OF(ALGORITHM) *algs;
|
---|
66 | /*
|
---|
67 | * Lock to protect the |algs| array from concurrent writing, when
|
---|
68 | * individual implementations or queries are inserted. This is used
|
---|
69 | * by the appropriate functions here.
|
---|
70 | */
|
---|
71 | CRYPTO_RWLOCK *lock;
|
---|
72 | /*
|
---|
73 | * Lock to reserve the whole store. This is used when fetching a set
|
---|
74 | * of algorithms, via these functions, found in crypto/core_fetch.c:
|
---|
75 | * ossl_method_construct_reserve_store()
|
---|
76 | * ossl_method_construct_unreserve_store()
|
---|
77 | */
|
---|
78 | CRYPTO_RWLOCK *biglock;
|
---|
79 |
|
---|
80 | /* query cache specific values */
|
---|
81 |
|
---|
82 | /* Count of the query cache entries for all algs */
|
---|
83 | size_t cache_nelem;
|
---|
84 |
|
---|
85 | /* Flag: 1 if query cache entries for all algs need flushing */
|
---|
86 | int cache_need_flush;
|
---|
87 | };
|
---|
88 |
|
---|
89 | typedef struct {
|
---|
90 | LHASH_OF(QUERY) *cache;
|
---|
91 | size_t nelem;
|
---|
92 | uint32_t seed;
|
---|
93 | unsigned char using_global_seed;
|
---|
94 | } IMPL_CACHE_FLUSH;
|
---|
95 |
|
---|
96 | DEFINE_SPARSE_ARRAY_OF(ALGORITHM);
|
---|
97 |
|
---|
98 | typedef struct ossl_global_properties_st {
|
---|
99 | OSSL_PROPERTY_LIST *list;
|
---|
100 | #ifndef FIPS_MODULE
|
---|
101 | unsigned int no_mirrored : 1;
|
---|
102 | #endif
|
---|
103 | } OSSL_GLOBAL_PROPERTIES;
|
---|
104 |
|
---|
105 | static void ossl_method_cache_flush_alg(OSSL_METHOD_STORE *store,
|
---|
106 | ALGORITHM *alg);
|
---|
107 | static void ossl_method_cache_flush(OSSL_METHOD_STORE *store, int nid);
|
---|
108 |
|
---|
109 | /* Global properties are stored per library context */
|
---|
110 | static void ossl_ctx_global_properties_free(void *vglobp)
|
---|
111 | {
|
---|
112 | OSSL_GLOBAL_PROPERTIES *globp = vglobp;
|
---|
113 |
|
---|
114 | if (globp != NULL) {
|
---|
115 | ossl_property_free(globp->list);
|
---|
116 | OPENSSL_free(globp);
|
---|
117 | }
|
---|
118 | }
|
---|
119 |
|
---|
120 | static void *ossl_ctx_global_properties_new(OSSL_LIB_CTX *ctx)
|
---|
121 | {
|
---|
122 | return OPENSSL_zalloc(sizeof(OSSL_GLOBAL_PROPERTIES));
|
---|
123 | }
|
---|
124 |
|
---|
125 | static const OSSL_LIB_CTX_METHOD ossl_ctx_global_properties_method = {
|
---|
126 | OSSL_LIB_CTX_METHOD_DEFAULT_PRIORITY,
|
---|
127 | ossl_ctx_global_properties_new,
|
---|
128 | ossl_ctx_global_properties_free,
|
---|
129 | };
|
---|
130 |
|
---|
131 | OSSL_PROPERTY_LIST **ossl_ctx_global_properties(OSSL_LIB_CTX *libctx,
|
---|
132 | int loadconfig)
|
---|
133 | {
|
---|
134 | OSSL_GLOBAL_PROPERTIES *globp;
|
---|
135 |
|
---|
136 | #ifndef FIPS_MODULE
|
---|
137 | if (loadconfig && !OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL))
|
---|
138 | return NULL;
|
---|
139 | #endif
|
---|
140 | globp = ossl_lib_ctx_get_data(libctx, OSSL_LIB_CTX_GLOBAL_PROPERTIES,
|
---|
141 | &ossl_ctx_global_properties_method);
|
---|
142 |
|
---|
143 | return globp != NULL ? &globp->list : NULL;
|
---|
144 | }
|
---|
145 |
|
---|
146 | #ifndef FIPS_MODULE
|
---|
147 | int ossl_global_properties_no_mirrored(OSSL_LIB_CTX *libctx)
|
---|
148 | {
|
---|
149 | OSSL_GLOBAL_PROPERTIES *globp
|
---|
150 | = ossl_lib_ctx_get_data(libctx, OSSL_LIB_CTX_GLOBAL_PROPERTIES,
|
---|
151 | &ossl_ctx_global_properties_method);
|
---|
152 |
|
---|
153 | return globp != NULL && globp->no_mirrored ? 1 : 0;
|
---|
154 | }
|
---|
155 |
|
---|
156 | void ossl_global_properties_stop_mirroring(OSSL_LIB_CTX *libctx)
|
---|
157 | {
|
---|
158 | OSSL_GLOBAL_PROPERTIES *globp
|
---|
159 | = ossl_lib_ctx_get_data(libctx, OSSL_LIB_CTX_GLOBAL_PROPERTIES,
|
---|
160 | &ossl_ctx_global_properties_method);
|
---|
161 |
|
---|
162 | if (globp != NULL)
|
---|
163 | globp->no_mirrored = 1;
|
---|
164 | }
|
---|
165 | #endif
|
---|
166 |
|
---|
167 | static int ossl_method_up_ref(METHOD *method)
|
---|
168 | {
|
---|
169 | return (*method->up_ref)(method->method);
|
---|
170 | }
|
---|
171 |
|
---|
172 | static void ossl_method_free(METHOD *method)
|
---|
173 | {
|
---|
174 | (*method->free)(method->method);
|
---|
175 | }
|
---|
176 |
|
---|
177 | static __owur int ossl_property_read_lock(OSSL_METHOD_STORE *p)
|
---|
178 | {
|
---|
179 | return p != NULL ? CRYPTO_THREAD_read_lock(p->lock) : 0;
|
---|
180 | }
|
---|
181 |
|
---|
182 | static __owur int ossl_property_write_lock(OSSL_METHOD_STORE *p)
|
---|
183 | {
|
---|
184 | return p != NULL ? CRYPTO_THREAD_write_lock(p->lock) : 0;
|
---|
185 | }
|
---|
186 |
|
---|
187 | static int ossl_property_unlock(OSSL_METHOD_STORE *p)
|
---|
188 | {
|
---|
189 | return p != 0 ? CRYPTO_THREAD_unlock(p->lock) : 0;
|
---|
190 | }
|
---|
191 |
|
---|
192 | static unsigned long query_hash(const QUERY *a)
|
---|
193 | {
|
---|
194 | return OPENSSL_LH_strhash(a->query);
|
---|
195 | }
|
---|
196 |
|
---|
197 | static int query_cmp(const QUERY *a, const QUERY *b)
|
---|
198 | {
|
---|
199 | int res = strcmp(a->query, b->query);
|
---|
200 |
|
---|
201 | if (res == 0 && a->provider != NULL && b->provider != NULL)
|
---|
202 | res = b->provider > a->provider ? 1
|
---|
203 | : b->provider < a->provider ? -1
|
---|
204 | : 0;
|
---|
205 | return res;
|
---|
206 | }
|
---|
207 |
|
---|
208 | static void impl_free(IMPLEMENTATION *impl)
|
---|
209 | {
|
---|
210 | if (impl != NULL) {
|
---|
211 | ossl_method_free(&impl->method);
|
---|
212 | OPENSSL_free(impl);
|
---|
213 | }
|
---|
214 | }
|
---|
215 |
|
---|
216 | static void impl_cache_free(QUERY *elem)
|
---|
217 | {
|
---|
218 | if (elem != NULL) {
|
---|
219 | ossl_method_free(&elem->method);
|
---|
220 | OPENSSL_free(elem);
|
---|
221 | }
|
---|
222 | }
|
---|
223 |
|
---|
224 | static void impl_cache_flush_alg(ossl_uintmax_t idx, ALGORITHM *alg)
|
---|
225 | {
|
---|
226 | lh_QUERY_doall(alg->cache, &impl_cache_free);
|
---|
227 | lh_QUERY_flush(alg->cache);
|
---|
228 | }
|
---|
229 |
|
---|
230 | static void alg_cleanup(ossl_uintmax_t idx, ALGORITHM *a, void *arg)
|
---|
231 | {
|
---|
232 | OSSL_METHOD_STORE *store = arg;
|
---|
233 |
|
---|
234 | if (a != NULL) {
|
---|
235 | sk_IMPLEMENTATION_pop_free(a->impls, &impl_free);
|
---|
236 | lh_QUERY_doall(a->cache, &impl_cache_free);
|
---|
237 | lh_QUERY_free(a->cache);
|
---|
238 | OPENSSL_free(a);
|
---|
239 | }
|
---|
240 | if (store != NULL)
|
---|
241 | ossl_sa_ALGORITHM_set(store->algs, idx, NULL);
|
---|
242 | }
|
---|
243 |
|
---|
244 | /*
|
---|
245 | * The OSSL_LIB_CTX param here allows access to underlying property data needed
|
---|
246 | * for computation
|
---|
247 | */
|
---|
248 | OSSL_METHOD_STORE *ossl_method_store_new(OSSL_LIB_CTX *ctx)
|
---|
249 | {
|
---|
250 | OSSL_METHOD_STORE *res;
|
---|
251 |
|
---|
252 | res = OPENSSL_zalloc(sizeof(*res));
|
---|
253 | if (res != NULL) {
|
---|
254 | res->ctx = ctx;
|
---|
255 | if ((res->algs = ossl_sa_ALGORITHM_new()) == NULL
|
---|
256 | || (res->lock = CRYPTO_THREAD_lock_new()) == NULL
|
---|
257 | || (res->biglock = CRYPTO_THREAD_lock_new()) == NULL) {
|
---|
258 | ossl_method_store_free(res);
|
---|
259 | return NULL;
|
---|
260 | }
|
---|
261 | }
|
---|
262 | return res;
|
---|
263 | }
|
---|
264 |
|
---|
265 | void ossl_method_store_free(OSSL_METHOD_STORE *store)
|
---|
266 | {
|
---|
267 | if (store != NULL) {
|
---|
268 | if (store->algs != NULL)
|
---|
269 | ossl_sa_ALGORITHM_doall_arg(store->algs, &alg_cleanup, store);
|
---|
270 | ossl_sa_ALGORITHM_free(store->algs);
|
---|
271 | CRYPTO_THREAD_lock_free(store->lock);
|
---|
272 | CRYPTO_THREAD_lock_free(store->biglock);
|
---|
273 | OPENSSL_free(store);
|
---|
274 | }
|
---|
275 | }
|
---|
276 |
|
---|
277 | int ossl_method_lock_store(OSSL_METHOD_STORE *store)
|
---|
278 | {
|
---|
279 | return store != NULL ? CRYPTO_THREAD_write_lock(store->biglock) : 0;
|
---|
280 | }
|
---|
281 |
|
---|
282 | int ossl_method_unlock_store(OSSL_METHOD_STORE *store)
|
---|
283 | {
|
---|
284 | return store != NULL ? CRYPTO_THREAD_unlock(store->biglock) : 0;
|
---|
285 | }
|
---|
286 |
|
---|
287 | static ALGORITHM *ossl_method_store_retrieve(OSSL_METHOD_STORE *store, int nid)
|
---|
288 | {
|
---|
289 | return ossl_sa_ALGORITHM_get(store->algs, nid);
|
---|
290 | }
|
---|
291 |
|
---|
292 | static int ossl_method_store_insert(OSSL_METHOD_STORE *store, ALGORITHM *alg)
|
---|
293 | {
|
---|
294 | return ossl_sa_ALGORITHM_set(store->algs, alg->nid, alg);
|
---|
295 | }
|
---|
296 |
|
---|
297 | int ossl_method_store_add(OSSL_METHOD_STORE *store, const OSSL_PROVIDER *prov,
|
---|
298 | int nid, const char *properties, void *method,
|
---|
299 | int (*method_up_ref)(void *),
|
---|
300 | void (*method_destruct)(void *))
|
---|
301 | {
|
---|
302 | ALGORITHM *alg = NULL;
|
---|
303 | IMPLEMENTATION *impl;
|
---|
304 | int ret = 0;
|
---|
305 | int i;
|
---|
306 |
|
---|
307 | if (nid <= 0 || method == NULL || store == NULL)
|
---|
308 | return 0;
|
---|
309 | if (properties == NULL)
|
---|
310 | properties = "";
|
---|
311 |
|
---|
312 | if (!ossl_assert(prov != NULL))
|
---|
313 | return 0;
|
---|
314 |
|
---|
315 | /* Create new entry */
|
---|
316 | impl = OPENSSL_malloc(sizeof(*impl));
|
---|
317 | if (impl == NULL)
|
---|
318 | return 0;
|
---|
319 | impl->method.method = method;
|
---|
320 | impl->method.up_ref = method_up_ref;
|
---|
321 | impl->method.free = method_destruct;
|
---|
322 | if (!ossl_method_up_ref(&impl->method)) {
|
---|
323 | OPENSSL_free(impl);
|
---|
324 | return 0;
|
---|
325 | }
|
---|
326 | impl->provider = prov;
|
---|
327 |
|
---|
328 | /* Insert into the hash table if required */
|
---|
329 | if (!ossl_property_write_lock(store)) {
|
---|
330 | OPENSSL_free(impl);
|
---|
331 | return 0;
|
---|
332 | }
|
---|
333 | ossl_method_cache_flush(store, nid);
|
---|
334 | if ((impl->properties = ossl_prop_defn_get(store->ctx, properties)) == NULL) {
|
---|
335 | impl->properties = ossl_parse_property(store->ctx, properties);
|
---|
336 | if (impl->properties == NULL)
|
---|
337 | goto err;
|
---|
338 | if (!ossl_prop_defn_set(store->ctx, properties, impl->properties)) {
|
---|
339 | ossl_property_free(impl->properties);
|
---|
340 | impl->properties = NULL;
|
---|
341 | goto err;
|
---|
342 | }
|
---|
343 | }
|
---|
344 |
|
---|
345 | alg = ossl_method_store_retrieve(store, nid);
|
---|
346 | if (alg == NULL) {
|
---|
347 | if ((alg = OPENSSL_zalloc(sizeof(*alg))) == NULL
|
---|
348 | || (alg->impls = sk_IMPLEMENTATION_new_null()) == NULL
|
---|
349 | || (alg->cache = lh_QUERY_new(&query_hash, &query_cmp)) == NULL)
|
---|
350 | goto err;
|
---|
351 | alg->nid = nid;
|
---|
352 | if (!ossl_method_store_insert(store, alg))
|
---|
353 | goto err;
|
---|
354 | }
|
---|
355 |
|
---|
356 | /* Push onto stack if there isn't one there already */
|
---|
357 | for (i = 0; i < sk_IMPLEMENTATION_num(alg->impls); i++) {
|
---|
358 | const IMPLEMENTATION *tmpimpl = sk_IMPLEMENTATION_value(alg->impls, i);
|
---|
359 |
|
---|
360 | if (tmpimpl->provider == impl->provider
|
---|
361 | && tmpimpl->properties == impl->properties)
|
---|
362 | break;
|
---|
363 | }
|
---|
364 | if (i == sk_IMPLEMENTATION_num(alg->impls)
|
---|
365 | && sk_IMPLEMENTATION_push(alg->impls, impl))
|
---|
366 | ret = 1;
|
---|
367 | ossl_property_unlock(store);
|
---|
368 | if (ret == 0)
|
---|
369 | impl_free(impl);
|
---|
370 | return ret;
|
---|
371 |
|
---|
372 | err:
|
---|
373 | ossl_property_unlock(store);
|
---|
374 | alg_cleanup(0, alg, NULL);
|
---|
375 | impl_free(impl);
|
---|
376 | return 0;
|
---|
377 | }
|
---|
378 |
|
---|
379 | int ossl_method_store_remove(OSSL_METHOD_STORE *store, int nid,
|
---|
380 | const void *method)
|
---|
381 | {
|
---|
382 | ALGORITHM *alg = NULL;
|
---|
383 | int i;
|
---|
384 |
|
---|
385 | if (nid <= 0 || method == NULL || store == NULL)
|
---|
386 | return 0;
|
---|
387 |
|
---|
388 | if (!ossl_property_write_lock(store))
|
---|
389 | return 0;
|
---|
390 | ossl_method_cache_flush(store, nid);
|
---|
391 | alg = ossl_method_store_retrieve(store, nid);
|
---|
392 | if (alg == NULL) {
|
---|
393 | ossl_property_unlock(store);
|
---|
394 | return 0;
|
---|
395 | }
|
---|
396 |
|
---|
397 | /*
|
---|
398 | * A sorting find then a delete could be faster but these stacks should be
|
---|
399 | * relatively small, so we avoid the overhead. Sorting could also surprise
|
---|
400 | * users when result orderings change (even though they are not guaranteed).
|
---|
401 | */
|
---|
402 | for (i = 0; i < sk_IMPLEMENTATION_num(alg->impls); i++) {
|
---|
403 | IMPLEMENTATION *impl = sk_IMPLEMENTATION_value(alg->impls, i);
|
---|
404 |
|
---|
405 | if (impl->method.method == method) {
|
---|
406 | impl_free(impl);
|
---|
407 | (void)sk_IMPLEMENTATION_delete(alg->impls, i);
|
---|
408 | ossl_property_unlock(store);
|
---|
409 | return 1;
|
---|
410 | }
|
---|
411 | }
|
---|
412 | ossl_property_unlock(store);
|
---|
413 | return 0;
|
---|
414 | }
|
---|
415 |
|
---|
416 | struct alg_cleanup_by_provider_data_st {
|
---|
417 | OSSL_METHOD_STORE *store;
|
---|
418 | const OSSL_PROVIDER *prov;
|
---|
419 | };
|
---|
420 |
|
---|
421 | static void
|
---|
422 | alg_cleanup_by_provider(ossl_uintmax_t idx, ALGORITHM *alg, void *arg)
|
---|
423 | {
|
---|
424 | struct alg_cleanup_by_provider_data_st *data = arg;
|
---|
425 | int i, count;
|
---|
426 |
|
---|
427 | /*
|
---|
428 | * We walk the stack backwards, to avoid having to deal with stack shifts
|
---|
429 | * caused by deletion
|
---|
430 | */
|
---|
431 | for (count = 0, i = sk_IMPLEMENTATION_num(alg->impls); i-- > 0;) {
|
---|
432 | IMPLEMENTATION *impl = sk_IMPLEMENTATION_value(alg->impls, i);
|
---|
433 |
|
---|
434 | if (impl->provider == data->prov) {
|
---|
435 | impl_free(impl);
|
---|
436 | (void)sk_IMPLEMENTATION_delete(alg->impls, i);
|
---|
437 | count++;
|
---|
438 | }
|
---|
439 | }
|
---|
440 |
|
---|
441 | /*
|
---|
442 | * If we removed any implementation, we also clear the whole associated
|
---|
443 | * cache, 'cause that's the sensible thing to do.
|
---|
444 | * There's no point flushing the cache entries where we didn't remove
|
---|
445 | * any implementation, though.
|
---|
446 | */
|
---|
447 | if (count > 0)
|
---|
448 | ossl_method_cache_flush_alg(data->store, alg);
|
---|
449 | }
|
---|
450 |
|
---|
451 | int ossl_method_store_remove_all_provided(OSSL_METHOD_STORE *store,
|
---|
452 | const OSSL_PROVIDER *prov)
|
---|
453 | {
|
---|
454 | struct alg_cleanup_by_provider_data_st data;
|
---|
455 |
|
---|
456 | if (!ossl_property_write_lock(store))
|
---|
457 | return 0;
|
---|
458 | data.prov = prov;
|
---|
459 | data.store = store;
|
---|
460 | ossl_sa_ALGORITHM_doall_arg(store->algs, &alg_cleanup_by_provider, &data);
|
---|
461 | ossl_property_unlock(store);
|
---|
462 | return 1;
|
---|
463 | }
|
---|
464 |
|
---|
465 | static void alg_do_one(ALGORITHM *alg, IMPLEMENTATION *impl,
|
---|
466 | void (*fn)(int id, void *method, void *fnarg),
|
---|
467 | void *fnarg)
|
---|
468 | {
|
---|
469 | fn(alg->nid, impl->method.method, fnarg);
|
---|
470 | }
|
---|
471 |
|
---|
472 | struct alg_do_each_data_st {
|
---|
473 | void (*fn)(int id, void *method, void *fnarg);
|
---|
474 | void *fnarg;
|
---|
475 | };
|
---|
476 |
|
---|
477 | static void alg_do_each(ossl_uintmax_t idx, ALGORITHM *alg, void *arg)
|
---|
478 | {
|
---|
479 | struct alg_do_each_data_st *data = arg;
|
---|
480 | int i, end = sk_IMPLEMENTATION_num(alg->impls);
|
---|
481 |
|
---|
482 | for (i = 0; i < end; i++) {
|
---|
483 | IMPLEMENTATION *impl = sk_IMPLEMENTATION_value(alg->impls, i);
|
---|
484 |
|
---|
485 | alg_do_one(alg, impl, data->fn, data->fnarg);
|
---|
486 | }
|
---|
487 | }
|
---|
488 |
|
---|
489 | void ossl_method_store_do_all(OSSL_METHOD_STORE *store,
|
---|
490 | void (*fn)(int id, void *method, void *fnarg),
|
---|
491 | void *fnarg)
|
---|
492 | {
|
---|
493 | struct alg_do_each_data_st data;
|
---|
494 |
|
---|
495 | data.fn = fn;
|
---|
496 | data.fnarg = fnarg;
|
---|
497 | if (store != NULL)
|
---|
498 | ossl_sa_ALGORITHM_doall_arg(store->algs, alg_do_each, &data);
|
---|
499 | }
|
---|
500 |
|
---|
501 | int ossl_method_store_fetch(OSSL_METHOD_STORE *store,
|
---|
502 | int nid, const char *prop_query,
|
---|
503 | const OSSL_PROVIDER **prov_rw, void **method)
|
---|
504 | {
|
---|
505 | OSSL_PROPERTY_LIST **plp;
|
---|
506 | ALGORITHM *alg;
|
---|
507 | IMPLEMENTATION *impl, *best_impl = NULL;
|
---|
508 | OSSL_PROPERTY_LIST *pq = NULL, *p2 = NULL;
|
---|
509 | const OSSL_PROVIDER *prov = prov_rw != NULL ? *prov_rw : NULL;
|
---|
510 | int ret = 0;
|
---|
511 | int j, best = -1, score, optional;
|
---|
512 |
|
---|
513 | #ifndef FIPS_MODULE
|
---|
514 | if (!OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL))
|
---|
515 | return 0;
|
---|
516 | #endif
|
---|
517 |
|
---|
518 | if (nid <= 0 || method == NULL || store == NULL)
|
---|
519 | return 0;
|
---|
520 |
|
---|
521 | /* This only needs to be a read lock, because the query won't create anything */
|
---|
522 | if (!ossl_property_read_lock(store))
|
---|
523 | return 0;
|
---|
524 | alg = ossl_method_store_retrieve(store, nid);
|
---|
525 | if (alg == NULL) {
|
---|
526 | ossl_property_unlock(store);
|
---|
527 | return 0;
|
---|
528 | }
|
---|
529 |
|
---|
530 | if (prop_query != NULL)
|
---|
531 | p2 = pq = ossl_parse_query(store->ctx, prop_query, 0);
|
---|
532 | plp = ossl_ctx_global_properties(store->ctx, 0);
|
---|
533 | if (plp != NULL && *plp != NULL) {
|
---|
534 | if (pq == NULL) {
|
---|
535 | pq = *plp;
|
---|
536 | } else {
|
---|
537 | p2 = ossl_property_merge(pq, *plp);
|
---|
538 | ossl_property_free(pq);
|
---|
539 | if (p2 == NULL)
|
---|
540 | goto fin;
|
---|
541 | pq = p2;
|
---|
542 | }
|
---|
543 | }
|
---|
544 |
|
---|
545 | if (pq == NULL) {
|
---|
546 | for (j = 0; j < sk_IMPLEMENTATION_num(alg->impls); j++) {
|
---|
547 | if ((impl = sk_IMPLEMENTATION_value(alg->impls, j)) != NULL
|
---|
548 | && (prov == NULL || impl->provider == prov)) {
|
---|
549 | best_impl = impl;
|
---|
550 | ret = 1;
|
---|
551 | break;
|
---|
552 | }
|
---|
553 | }
|
---|
554 | goto fin;
|
---|
555 | }
|
---|
556 | optional = ossl_property_has_optional(pq);
|
---|
557 | for (j = 0; j < sk_IMPLEMENTATION_num(alg->impls); j++) {
|
---|
558 | if ((impl = sk_IMPLEMENTATION_value(alg->impls, j)) != NULL
|
---|
559 | && (prov == NULL || impl->provider == prov)) {
|
---|
560 | score = ossl_property_match_count(pq, impl->properties);
|
---|
561 | if (score > best) {
|
---|
562 | best_impl = impl;
|
---|
563 | best = score;
|
---|
564 | ret = 1;
|
---|
565 | if (!optional)
|
---|
566 | goto fin;
|
---|
567 | }
|
---|
568 | }
|
---|
569 | }
|
---|
570 | fin:
|
---|
571 | if (ret && ossl_method_up_ref(&best_impl->method)) {
|
---|
572 | *method = best_impl->method.method;
|
---|
573 | if (prov_rw != NULL)
|
---|
574 | *prov_rw = best_impl->provider;
|
---|
575 | } else {
|
---|
576 | ret = 0;
|
---|
577 | }
|
---|
578 | ossl_property_unlock(store);
|
---|
579 | ossl_property_free(p2);
|
---|
580 | return ret;
|
---|
581 | }
|
---|
582 |
|
---|
583 | static void ossl_method_cache_flush_alg(OSSL_METHOD_STORE *store,
|
---|
584 | ALGORITHM *alg)
|
---|
585 | {
|
---|
586 | store->cache_nelem -= lh_QUERY_num_items(alg->cache);
|
---|
587 | impl_cache_flush_alg(0, alg);
|
---|
588 | }
|
---|
589 |
|
---|
590 | static void ossl_method_cache_flush(OSSL_METHOD_STORE *store, int nid)
|
---|
591 | {
|
---|
592 | ALGORITHM *alg = ossl_method_store_retrieve(store, nid);
|
---|
593 |
|
---|
594 | if (alg != NULL)
|
---|
595 | ossl_method_cache_flush_alg(store, alg);
|
---|
596 | }
|
---|
597 |
|
---|
598 | int ossl_method_store_cache_flush_all(OSSL_METHOD_STORE *store)
|
---|
599 | {
|
---|
600 | if (!ossl_property_write_lock(store))
|
---|
601 | return 0;
|
---|
602 | ossl_sa_ALGORITHM_doall(store->algs, &impl_cache_flush_alg);
|
---|
603 | store->cache_nelem = 0;
|
---|
604 | ossl_property_unlock(store);
|
---|
605 | return 1;
|
---|
606 | }
|
---|
607 |
|
---|
608 | IMPLEMENT_LHASH_DOALL_ARG(QUERY, IMPL_CACHE_FLUSH);
|
---|
609 |
|
---|
610 | /*
|
---|
611 | * Flush an element from the query cache (perhaps).
|
---|
612 | *
|
---|
613 | * In order to avoid taking a write lock or using atomic operations
|
---|
614 | * to keep accurate least recently used (LRU) or least frequently used
|
---|
615 | * (LFU) information, the procedure used here is to stochastically
|
---|
616 | * flush approximately half the cache.
|
---|
617 | *
|
---|
618 | * This procedure isn't ideal, LRU or LFU would be better. However,
|
---|
619 | * in normal operation, reaching a full cache would be unexpected.
|
---|
620 | * It means that no steady state of algorithm queries has been reached.
|
---|
621 | * That is, it is most likely an attack of some form. A suboptimal clearance
|
---|
622 | * strategy that doesn't degrade performance of the normal case is
|
---|
623 | * preferable to a more refined approach that imposes a performance
|
---|
624 | * impact.
|
---|
625 | */
|
---|
626 | static void impl_cache_flush_cache(QUERY *c, IMPL_CACHE_FLUSH *state)
|
---|
627 | {
|
---|
628 | uint32_t n;
|
---|
629 |
|
---|
630 | /*
|
---|
631 | * Implement the 32 bit xorshift as suggested by George Marsaglia in:
|
---|
632 | * https://doi.org/10.18637/jss.v008.i14
|
---|
633 | *
|
---|
634 | * This is a very fast PRNG so there is no need to extract bits one at a
|
---|
635 | * time and use the entire value each time.
|
---|
636 | */
|
---|
637 | n = state->seed;
|
---|
638 | n ^= n << 13;
|
---|
639 | n ^= n >> 17;
|
---|
640 | n ^= n << 5;
|
---|
641 | state->seed = n;
|
---|
642 |
|
---|
643 | if ((n & 1) != 0)
|
---|
644 | impl_cache_free(lh_QUERY_delete(state->cache, c));
|
---|
645 | else
|
---|
646 | state->nelem++;
|
---|
647 | }
|
---|
648 |
|
---|
649 | static void impl_cache_flush_one_alg(ossl_uintmax_t idx, ALGORITHM *alg,
|
---|
650 | void *v)
|
---|
651 | {
|
---|
652 | IMPL_CACHE_FLUSH *state = (IMPL_CACHE_FLUSH *)v;
|
---|
653 |
|
---|
654 | state->cache = alg->cache;
|
---|
655 | lh_QUERY_doall_IMPL_CACHE_FLUSH(state->cache, &impl_cache_flush_cache,
|
---|
656 | state);
|
---|
657 | }
|
---|
658 |
|
---|
659 | static void ossl_method_cache_flush_some(OSSL_METHOD_STORE *store)
|
---|
660 | {
|
---|
661 | IMPL_CACHE_FLUSH state;
|
---|
662 | static TSAN_QUALIFIER uint32_t global_seed = 1;
|
---|
663 |
|
---|
664 | state.nelem = 0;
|
---|
665 | state.using_global_seed = 0;
|
---|
666 | if ((state.seed = OPENSSL_rdtsc()) == 0) {
|
---|
667 | /* If there is no timer available, seed another way */
|
---|
668 | state.using_global_seed = 1;
|
---|
669 | state.seed = tsan_load(&global_seed);
|
---|
670 | }
|
---|
671 | store->cache_need_flush = 0;
|
---|
672 | ossl_sa_ALGORITHM_doall_arg(store->algs, &impl_cache_flush_one_alg, &state);
|
---|
673 | store->cache_nelem = state.nelem;
|
---|
674 | /* Without a timer, update the global seed */
|
---|
675 | if (state.using_global_seed)
|
---|
676 | tsan_store(&global_seed, state.seed);
|
---|
677 | }
|
---|
678 |
|
---|
679 | int ossl_method_store_cache_get(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov,
|
---|
680 | int nid, const char *prop_query, void **method)
|
---|
681 | {
|
---|
682 | ALGORITHM *alg;
|
---|
683 | QUERY elem, *r;
|
---|
684 | int res = 0;
|
---|
685 |
|
---|
686 | if (nid <= 0 || store == NULL || prop_query == NULL)
|
---|
687 | return 0;
|
---|
688 |
|
---|
689 | if (!ossl_property_read_lock(store))
|
---|
690 | return 0;
|
---|
691 | alg = ossl_method_store_retrieve(store, nid);
|
---|
692 | if (alg == NULL)
|
---|
693 | goto err;
|
---|
694 |
|
---|
695 | elem.query = prop_query;
|
---|
696 | elem.provider = prov;
|
---|
697 | r = lh_QUERY_retrieve(alg->cache, &elem);
|
---|
698 | if (r == NULL)
|
---|
699 | goto err;
|
---|
700 | if (ossl_method_up_ref(&r->method)) {
|
---|
701 | *method = r->method.method;
|
---|
702 | res = 1;
|
---|
703 | }
|
---|
704 | err:
|
---|
705 | ossl_property_unlock(store);
|
---|
706 | return res;
|
---|
707 | }
|
---|
708 |
|
---|
709 | int ossl_method_store_cache_set(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov,
|
---|
710 | int nid, const char *prop_query, void *method,
|
---|
711 | int (*method_up_ref)(void *),
|
---|
712 | void (*method_destruct)(void *))
|
---|
713 | {
|
---|
714 | QUERY elem, *old, *p = NULL;
|
---|
715 | ALGORITHM *alg;
|
---|
716 | size_t len;
|
---|
717 | int res = 1;
|
---|
718 |
|
---|
719 | if (nid <= 0 || store == NULL || prop_query == NULL)
|
---|
720 | return 0;
|
---|
721 |
|
---|
722 | if (!ossl_assert(prov != NULL))
|
---|
723 | return 0;
|
---|
724 |
|
---|
725 | if (!ossl_property_write_lock(store))
|
---|
726 | return 0;
|
---|
727 | if (store->cache_need_flush)
|
---|
728 | ossl_method_cache_flush_some(store);
|
---|
729 | alg = ossl_method_store_retrieve(store, nid);
|
---|
730 | if (alg == NULL)
|
---|
731 | goto err;
|
---|
732 |
|
---|
733 | if (method == NULL) {
|
---|
734 | elem.query = prop_query;
|
---|
735 | elem.provider = prov;
|
---|
736 | if ((old = lh_QUERY_delete(alg->cache, &elem)) != NULL) {
|
---|
737 | impl_cache_free(old);
|
---|
738 | store->cache_nelem--;
|
---|
739 | }
|
---|
740 | goto end;
|
---|
741 | }
|
---|
742 | p = OPENSSL_malloc(sizeof(*p) + (len = strlen(prop_query)));
|
---|
743 | if (p != NULL) {
|
---|
744 | p->query = p->body;
|
---|
745 | p->provider = prov;
|
---|
746 | p->method.method = method;
|
---|
747 | p->method.up_ref = method_up_ref;
|
---|
748 | p->method.free = method_destruct;
|
---|
749 | if (!ossl_method_up_ref(&p->method))
|
---|
750 | goto err;
|
---|
751 | memcpy((char *)p->query, prop_query, len + 1);
|
---|
752 | if ((old = lh_QUERY_insert(alg->cache, p)) != NULL) {
|
---|
753 | impl_cache_free(old);
|
---|
754 | goto end;
|
---|
755 | }
|
---|
756 | if (!lh_QUERY_error(alg->cache)) {
|
---|
757 | if (++store->cache_nelem >= IMPL_CACHE_FLUSH_THRESHOLD)
|
---|
758 | store->cache_need_flush = 1;
|
---|
759 | goto end;
|
---|
760 | }
|
---|
761 | ossl_method_free(&p->method);
|
---|
762 | }
|
---|
763 | err:
|
---|
764 | res = 0;
|
---|
765 | OPENSSL_free(p);
|
---|
766 | end:
|
---|
767 | ossl_property_unlock(store);
|
---|
768 | return res;
|
---|
769 | }
|
---|