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