]> git.ipfire.org Git - thirdparty/openssl.git/blame - crypto/property/property.c
Update copyright year
[thirdparty/openssl.git] / crypto / property / property.c
CommitLineData
1bdbdaff 1/*
3c2bdd7d 2 * Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved.
1bdbdaff
P
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/property.h"
25f2138b 16#include "crypto/ctype.h"
1bdbdaff
P
17#include <openssl/lhash.h>
18#include <openssl/rand.h>
19#include "internal/thread_once.h"
25f2138b
DMSP
20#include "crypto/lhash.h"
21#include "crypto/sparse_array.h"
706457b7 22#include "property_local.h"
1bdbdaff 23
e5ecfcc7
P
24/*
25 * The number of elements in the query cache before we initiate a flush.
26 * If reducing this, also ensure the stochastic test in test/property_test.c
27 * isn't likely to fail.
28 */
29#define IMPL_CACHE_FLUSH_THRESHOLD 500
bdbf2df2
P
30
31typedef struct {
32 void *method;
33 int (*up_ref)(void *);
34 void (*free)(void *);
35} METHOD;
1bdbdaff
P
36
37typedef struct {
c1d56231 38 const OSSL_PROVIDER *provider;
1bdbdaff 39 OSSL_PROPERTY_LIST *properties;
bdbf2df2 40 METHOD method;
1bdbdaff
P
41} IMPLEMENTATION;
42
43DEFINE_STACK_OF(IMPLEMENTATION)
44
45typedef struct {
46 const char *query;
bdbf2df2 47 METHOD method;
1bdbdaff
P
48 char body[1];
49} QUERY;
50
51DEFINE_LHASH_OF(QUERY);
52
53typedef struct {
54 int nid;
55 STACK_OF(IMPLEMENTATION) *impls;
56 LHASH_OF(QUERY) *cache;
57} ALGORITHM;
58
59struct ossl_method_store_st {
b4250010 60 OSSL_LIB_CTX *ctx;
1bdbdaff
P
61 size_t nelem;
62 SPARSE_ARRAY_OF(ALGORITHM) *algs;
1bdbdaff 63 int need_flush;
1bdbdaff
P
64 CRYPTO_RWLOCK *lock;
65};
66
67typedef struct {
1bdbdaff
P
68 LHASH_OF(QUERY) *cache;
69 size_t nelem;
f06cf3c4 70 uint32_t seed;
1bdbdaff
P
71} IMPL_CACHE_FLUSH;
72
73DEFINE_SPARSE_ARRAY_OF(ALGORITHM);
74
75static void ossl_method_cache_flush(OSSL_METHOD_STORE *store, int nid);
f9e504e8
P
76
77/* Global properties are stored per library context */
78static void ossl_ctx_global_properties_free(void *vstore)
79{
80 OSSL_PROPERTY_LIST **plp = vstore;
81
82 if (plp != NULL) {
83 ossl_property_free(*plp);
84 OPENSSL_free(plp);
85 }
86}
87
b4250010 88static void *ossl_ctx_global_properties_new(OSSL_LIB_CTX *ctx)
f9e504e8
P
89{
90 return OPENSSL_zalloc(sizeof(OSSL_PROPERTY_LIST **));
91}
92
93
b4250010 94static const OSSL_LIB_CTX_METHOD ossl_ctx_global_properties_method = {
f9e504e8
P
95 ossl_ctx_global_properties_new,
96 ossl_ctx_global_properties_free,
97};
98
b4250010 99OSSL_PROPERTY_LIST **ossl_ctx_global_properties(OSSL_LIB_CTX *libctx,
e6c54619 100 int loadconfig)
f9e504e8 101{
e6c54619
MC
102#ifndef FIPS_MODULE
103 if (loadconfig && !OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL))
104 return NULL;
105#endif
b4250010
DMSP
106 return ossl_lib_ctx_get_data(libctx, OSSL_LIB_CTX_GLOBAL_PROPERTIES,
107 &ossl_ctx_global_properties_method);
f9e504e8 108}
1bdbdaff 109
bdbf2df2
P
110static int ossl_method_up_ref(METHOD *method)
111{
112 return (*method->up_ref)(method->method);
113}
114
115static void ossl_method_free(METHOD *method)
116{
117 (*method->free)(method->method);
118}
119
860ecfd7 120static __owur int ossl_property_read_lock(OSSL_METHOD_STORE *p)
1bdbdaff
P
121{
122 return p != NULL ? CRYPTO_THREAD_read_lock(p->lock) : 0;
123}
124
860ecfd7 125static __owur int ossl_property_write_lock(OSSL_METHOD_STORE *p)
1bdbdaff
P
126{
127 return p != NULL ? CRYPTO_THREAD_write_lock(p->lock) : 0;
128}
129
860ecfd7 130static int ossl_property_unlock(OSSL_METHOD_STORE *p)
1bdbdaff
P
131{
132 return p != 0 ? CRYPTO_THREAD_unlock(p->lock) : 0;
133}
134
1bdbdaff
P
135static unsigned long query_hash(const QUERY *a)
136{
137 return OPENSSL_LH_strhash(a->query);
138}
139
140static int query_cmp(const QUERY *a, const QUERY *b)
141{
142 return strcmp(a->query, b->query);
143}
144
145static void impl_free(IMPLEMENTATION *impl)
146{
147 if (impl != NULL) {
bdbf2df2 148 ossl_method_free(&impl->method);
1bdbdaff
P
149 OPENSSL_free(impl);
150 }
151}
152
153static void impl_cache_free(QUERY *elem)
154{
bdbf2df2
P
155 if (elem != NULL) {
156 ossl_method_free(&elem->method);
157 OPENSSL_free(elem);
158 }
1bdbdaff
P
159}
160
8ab53b19 161static void alg_cleanup(ossl_uintmax_t idx, ALGORITHM *a)
1bdbdaff
P
162{
163 if (a != NULL) {
164 sk_IMPLEMENTATION_pop_free(a->impls, &impl_free);
165 lh_QUERY_doall(a->cache, &impl_cache_free);
166 lh_QUERY_free(a->cache);
167 OPENSSL_free(a);
168 }
169}
170
1aedc35f 171/*
b4250010 172 * The OSSL_LIB_CTX param here allows access to underlying property data needed
1aedc35f
MC
173 * for computation
174 */
b4250010 175OSSL_METHOD_STORE *ossl_method_store_new(OSSL_LIB_CTX *ctx)
1bdbdaff 176{
909f2e59 177 OSSL_METHOD_STORE *res;
1bdbdaff 178
909f2e59 179 res = OPENSSL_zalloc(sizeof(*res));
1bdbdaff 180 if (res != NULL) {
1aedc35f 181 res->ctx = ctx;
1bdbdaff
P
182 if ((res->algs = ossl_sa_ALGORITHM_new()) == NULL) {
183 OPENSSL_free(res);
184 return NULL;
185 }
186 if ((res->lock = CRYPTO_THREAD_lock_new()) == NULL) {
bdbf2df2 187 ossl_sa_ALGORITHM_free(res->algs);
1bdbdaff
P
188 OPENSSL_free(res);
189 return NULL;
190 }
191 }
192 return res;
193}
194
195void ossl_method_store_free(OSSL_METHOD_STORE *store)
196{
197 if (store != NULL) {
198 ossl_sa_ALGORITHM_doall(store->algs, &alg_cleanup);
199 ossl_sa_ALGORITHM_free(store->algs);
1bdbdaff
P
200 CRYPTO_THREAD_lock_free(store->lock);
201 OPENSSL_free(store);
202 }
203}
204
205static ALGORITHM *ossl_method_store_retrieve(OSSL_METHOD_STORE *store, int nid)
206{
207 return ossl_sa_ALGORITHM_get(store->algs, nid);
208}
209
210static int ossl_method_store_insert(OSSL_METHOD_STORE *store, ALGORITHM *alg)
211{
212 return ossl_sa_ALGORITHM_set(store->algs, alg->nid, alg);
213}
214
c1d56231 215int ossl_method_store_add(OSSL_METHOD_STORE *store, const OSSL_PROVIDER *prov,
b1d40ddf
RL
216 int nid, const char *properties, void *method,
217 int (*method_up_ref)(void *),
218 void (*method_destruct)(void *))
1bdbdaff
P
219{
220 ALGORITHM *alg = NULL;
221 IMPLEMENTATION *impl;
222 int ret = 0;
c1d56231 223 int i;
1bdbdaff 224
0a79572a 225 if (nid <= 0 || method == NULL || store == NULL)
1bdbdaff
P
226 return 0;
227 if (properties == NULL)
228 properties = "";
229
230 /* Create new entry */
231 impl = OPENSSL_malloc(sizeof(*impl));
232 if (impl == NULL)
233 return 0;
bdbf2df2
P
234 impl->method.method = method;
235 impl->method.up_ref = method_up_ref;
236 impl->method.free = method_destruct;
237 if (!ossl_method_up_ref(&impl->method)) {
c1d56231 238 OPENSSL_free(impl);
b1d40ddf 239 return 0;
c1d56231
RL
240 }
241 impl->provider = prov;
1bdbdaff
P
242
243 /*
244 * Insert into the hash table if required.
245 *
246 * A write lock is used unconditionally because we wend our way down to the
247 * property string code which isn't locking friendly.
248 */
860ecfd7
P
249 if (!ossl_property_write_lock(store)) {
250 OPENSSL_free(impl);
251 return 0;
252 }
1bdbdaff 253 ossl_method_cache_flush(store, nid);
1aedc35f
MC
254 if ((impl->properties = ossl_prop_defn_get(store->ctx, properties)) == NULL) {
255 impl->properties = ossl_parse_property(store->ctx, properties);
256 if (impl->properties == NULL)
1bdbdaff 257 goto err;
1aedc35f 258 ossl_prop_defn_set(store->ctx, properties, impl->properties);
1bdbdaff
P
259 }
260
261 alg = ossl_method_store_retrieve(store, nid);
262 if (alg == NULL) {
263 if ((alg = OPENSSL_zalloc(sizeof(*alg))) == NULL
264 || (alg->impls = sk_IMPLEMENTATION_new_null()) == NULL
265 || (alg->cache = lh_QUERY_new(&query_hash, &query_cmp)) == NULL)
266 goto err;
267 alg->nid = nid;
268 if (!ossl_method_store_insert(store, alg))
269 goto err;
270 }
271
c1d56231
RL
272 /* Push onto stack if there isn't one there already */
273 for (i = 0; i < sk_IMPLEMENTATION_num(alg->impls); i++) {
274 const IMPLEMENTATION *tmpimpl = sk_IMPLEMENTATION_value(alg->impls, i);
275
276 if (tmpimpl->provider == impl->provider
277 && tmpimpl->properties == impl->properties)
278 break;
279 }
280 if (i == sk_IMPLEMENTATION_num(alg->impls)
281 && sk_IMPLEMENTATION_push(alg->impls, impl))
1bdbdaff
P
282 ret = 1;
283 ossl_property_unlock(store);
284 if (ret == 0)
285 impl_free(impl);
286 return ret;
287
288err:
289 ossl_property_unlock(store);
290 alg_cleanup(0, alg);
291 impl_free(impl);
292 return 0;
293}
294
295int ossl_method_store_remove(OSSL_METHOD_STORE *store, int nid,
0a79572a 296 const void *method)
1bdbdaff
P
297{
298 ALGORITHM *alg = NULL;
299 int i;
300
0a79572a 301 if (nid <= 0 || method == NULL || store == NULL)
1bdbdaff
P
302 return 0;
303
860ecfd7
P
304 if (!ossl_property_write_lock(store))
305 return 0;
1bdbdaff
P
306 ossl_method_cache_flush(store, nid);
307 alg = ossl_method_store_retrieve(store, nid);
308 if (alg == NULL) {
309 ossl_property_unlock(store);
310 return 0;
311 }
312
313 /*
314 * A sorting find then a delete could be faster but these stacks should be
315 * relatively small, so we avoid the overhead. Sorting could also surprise
316 * users when result orderings change (even though they are not guaranteed).
317 */
318 for (i = 0; i < sk_IMPLEMENTATION_num(alg->impls); i++) {
319 IMPLEMENTATION *impl = sk_IMPLEMENTATION_value(alg->impls, i);
320
bdbf2df2
P
321 if (impl->method.method == method) {
322 impl_free(impl);
225c9660 323 (void)sk_IMPLEMENTATION_delete(alg->impls, i);
1bdbdaff 324 ossl_property_unlock(store);
1bdbdaff
P
325 return 1;
326 }
327 }
328 ossl_property_unlock(store);
329 return 0;
330}
331
332int ossl_method_store_fetch(OSSL_METHOD_STORE *store, int nid,
f9e504e8
P
333 const char *prop_query,
334 void **method)
1bdbdaff 335{
6f924bb8 336 OSSL_PROPERTY_LIST **plp;
1bdbdaff
P
337 ALGORITHM *alg;
338 IMPLEMENTATION *impl;
f9e504e8 339 OSSL_PROPERTY_LIST *pq = NULL, *p2 = NULL;
bdbf2df2 340 METHOD *best_method = NULL;
1bdbdaff 341 int ret = 0;
da89ac0b 342 int j, best = -1, score, optional;
1bdbdaff 343
f844f9eb 344#ifndef FIPS_MODULE
07af9441
BE
345 if (!OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL))
346 return 0;
29dc6e00
MC
347#endif
348
0a79572a 349 if (nid <= 0 || method == NULL || store == NULL)
1bdbdaff
P
350 return 0;
351
352 /*
353 * This only needs to be a read lock, because queries never create property
354 * names or value and thus don't modify any of the property string layer.
355 */
860ecfd7
P
356 if (!ossl_property_read_lock(store))
357 return 0;
1bdbdaff
P
358 alg = ossl_method_store_retrieve(store, nid);
359 if (alg == NULL) {
360 ossl_property_unlock(store);
361 return 0;
362 }
363
6f924bb8 364 if (prop_query != NULL)
1e08f3ba 365 p2 = pq = ossl_parse_query(store->ctx, prop_query, 0);
07af9441 366 plp = ossl_ctx_global_properties(store->ctx, 0);
f9e504e8
P
367 if (plp != NULL && *plp != NULL) {
368 if (pq == NULL) {
369 pq = *plp;
370 } else {
371 p2 = ossl_property_merge(pq, *plp);
84ba665d 372 ossl_property_free(pq);
f9e504e8
P
373 if (p2 == NULL)
374 goto fin;
f9e504e8
P
375 pq = p2;
376 }
377 }
378
379 if (pq == NULL) {
1bdbdaff 380 if ((impl = sk_IMPLEMENTATION_value(alg->impls, 0)) != NULL) {
bdbf2df2 381 best_method = &impl->method;
1bdbdaff
P
382 ret = 1;
383 }
384 goto fin;
385 }
da89ac0b 386 optional = ossl_property_has_optional(pq);
1bdbdaff
P
387 for (j = 0; j < sk_IMPLEMENTATION_num(alg->impls); j++) {
388 impl = sk_IMPLEMENTATION_value(alg->impls, j);
da89ac0b
P
389 score = ossl_property_match_count(pq, impl->properties);
390 if (score > best) {
bdbf2df2
P
391 best_method = &impl->method;
392 best = score;
1bdbdaff 393 ret = 1;
da89ac0b
P
394 if (!optional)
395 goto fin;
1bdbdaff
P
396 }
397 }
398fin:
bdbf2df2
P
399 if (ret && ossl_method_up_ref(best_method))
400 *method = best_method->method;
401 else
402 ret = 0;
1bdbdaff 403 ossl_property_unlock(store);
f9e504e8 404 ossl_property_free(p2);
1bdbdaff
P
405 return ret;
406}
407
04cb5ec0 408static void impl_cache_flush_alg(ossl_uintmax_t idx, ALGORITHM *alg, void *arg)
1bdbdaff 409{
04cb5ec0
SL
410 SPARSE_ARRAY_OF(ALGORITHM) *algs = arg;
411
1bdbdaff 412 lh_QUERY_doall(alg->cache, &impl_cache_free);
04cb5ec0
SL
413 if (algs != NULL) {
414 sk_IMPLEMENTATION_pop_free(alg->impls, &impl_free);
415 lh_QUERY_free(alg->cache);
416 OPENSSL_free(alg);
417 ossl_sa_ALGORITHM_set(algs, idx, NULL);
418 } else {
419 lh_QUERY_flush(alg->cache);
420 }
1bdbdaff
P
421}
422
423static void ossl_method_cache_flush(OSSL_METHOD_STORE *store, int nid)
424{
425 ALGORITHM *alg = ossl_method_store_retrieve(store, nid);
426
427 if (alg != NULL) {
428 store->nelem -= lh_QUERY_num_items(alg->cache);
04cb5ec0 429 impl_cache_flush_alg(0, alg, NULL);
1bdbdaff
P
430 }
431}
432
860ecfd7 433int ossl_method_store_flush_cache(OSSL_METHOD_STORE *store, int all)
1bdbdaff 434{
04cb5ec0
SL
435 void *arg = (all != 0 ? store->algs : NULL);
436
860ecfd7
P
437 if (!ossl_property_write_lock(store))
438 return 0;
04cb5ec0 439 ossl_sa_ALGORITHM_doall_arg(store->algs, &impl_cache_flush_alg, arg);
1bdbdaff 440 store->nelem = 0;
f9e504e8 441 ossl_property_unlock(store);
860ecfd7 442 return 1;
1bdbdaff
P
443}
444
445IMPLEMENT_LHASH_DOALL_ARG(QUERY, IMPL_CACHE_FLUSH);
446
447/*
448 * Flush an element from the query cache (perhaps).
449 *
f06cf3c4
P
450 * In order to avoid taking a write lock or using atomic operations
451 * to keep accurate least recently used (LRU) or least frequently used
452 * (LFU) information, the procedure used here is to stochastically
453 * flush approximately half the cache.
1bdbdaff 454 *
f06cf3c4
P
455 * This procedure isn't ideal, LRU or LFU would be better. However,
456 * in normal operation, reaching a full cache would be unexpected.
457 * It means that no steady state of algorithm queries has been reached.
458 * That is, it is most likely an attack of some form. A suboptimal clearance
459 * strategy that doesn't degrade performance of the normal case is
460 * preferable to a more refined approach that imposes a performance
461 * impact.
1bdbdaff
P
462 */
463static void impl_cache_flush_cache(QUERY *c, IMPL_CACHE_FLUSH *state)
464{
f06cf3c4
P
465 uint32_t n;
466
467 /*
468 * Implement the 32 bit xorshift as suggested by George Marsaglia in:
469 * https://doi.org/10.18637/jss.v008.i14
470 *
471 * This is a very fast PRNG so there is no need to extract bits one at a
472 * time and use the entire value each time.
473 */
474 n = state->seed;
475 n ^= n << 13;
476 n ^= n >> 17;
477 n ^= n << 5;
478 state->seed = n;
479
480 if ((n & 1) != 0)
bdbf2df2 481 impl_cache_free(lh_QUERY_delete(state->cache, c));
1bdbdaff
P
482 else
483 state->nelem++;
484}
485
8ab53b19
P
486static void impl_cache_flush_one_alg(ossl_uintmax_t idx, ALGORITHM *alg,
487 void *v)
1bdbdaff
P
488{
489 IMPL_CACHE_FLUSH *state = (IMPL_CACHE_FLUSH *)v;
490
491 state->cache = alg->cache;
492 lh_QUERY_doall_IMPL_CACHE_FLUSH(state->cache, &impl_cache_flush_cache,
493 state);
494}
495
496static void ossl_method_cache_flush_some(OSSL_METHOD_STORE *store)
497{
498 IMPL_CACHE_FLUSH state;
499
500 state.nelem = 0;
f06cf3c4
P
501 if ((state.seed = OPENSSL_rdtsc()) == 0)
502 state.seed = 1;
1bdbdaff 503 store->need_flush = 0;
e2e5abe4 504 ossl_sa_ALGORITHM_doall_arg(store->algs, &impl_cache_flush_one_alg, &state);
1bdbdaff
P
505 store->nelem = state.nelem;
506}
507
508int ossl_method_store_cache_get(OSSL_METHOD_STORE *store, int nid,
0a79572a 509 const char *prop_query, void **method)
1bdbdaff
P
510{
511 ALGORITHM *alg;
512 QUERY elem, *r;
bdbf2df2 513 int res = 0;
1bdbdaff
P
514
515 if (nid <= 0 || store == NULL)
516 return 0;
517
860ecfd7
P
518 if (!ossl_property_read_lock(store))
519 return 0;
1bdbdaff 520 alg = ossl_method_store_retrieve(store, nid);
bdbf2df2
P
521 if (alg == NULL)
522 goto err;
1bdbdaff 523
1393722a 524 elem.query = prop_query != NULL ? prop_query : "";
1bdbdaff 525 r = lh_QUERY_retrieve(alg->cache, &elem);
bdbf2df2
P
526 if (r == NULL)
527 goto err;
528 if (ossl_method_up_ref(&r->method)) {
529 *method = r->method.method;
530 res = 1;
1bdbdaff 531 }
bdbf2df2 532err:
1bdbdaff 533 ossl_property_unlock(store);
bdbf2df2 534 return res;
1bdbdaff
P
535}
536
537int ossl_method_store_cache_set(OSSL_METHOD_STORE *store, int nid,
bdbf2df2
P
538 const char *prop_query, void *method,
539 int (*method_up_ref)(void *),
540 void (*method_destruct)(void *))
1bdbdaff
P
541{
542 QUERY elem, *old, *p = NULL;
543 ALGORITHM *alg;
544 size_t len;
bdbf2df2 545 int res = 1;
1bdbdaff
P
546
547 if (nid <= 0 || store == NULL)
548 return 0;
549 if (prop_query == NULL)
550 return 1;
551
860ecfd7
P
552 if (!ossl_property_write_lock(store))
553 return 0;
1bdbdaff
P
554 if (store->need_flush)
555 ossl_method_cache_flush_some(store);
556 alg = ossl_method_store_retrieve(store, nid);
bdbf2df2
P
557 if (alg == NULL)
558 goto err;
1bdbdaff 559
0a79572a 560 if (method == NULL) {
1bdbdaff 561 elem.query = prop_query;
bdbf2df2
P
562 if ((old = lh_QUERY_delete(alg->cache, &elem)) != NULL) {
563 impl_cache_free(old);
f06cf3c4 564 store->nelem--;
bdbf2df2
P
565 }
566 goto end;
1bdbdaff
P
567 }
568 p = OPENSSL_malloc(sizeof(*p) + (len = strlen(prop_query)));
569 if (p != NULL) {
570 p->query = p->body;
bdbf2df2
P
571 p->method.method = method;
572 p->method.up_ref = method_up_ref;
573 p->method.free = method_destruct;
574 if (!ossl_method_up_ref(&p->method))
575 goto err;
1bdbdaff 576 memcpy((char *)p->query, prop_query, len + 1);
f06cf3c4 577 if ((old = lh_QUERY_insert(alg->cache, p)) != NULL) {
bdbf2df2
P
578 impl_cache_free(old);
579 goto end;
f06cf3c4
P
580 }
581 if (!lh_QUERY_error(alg->cache)) {
582 if (++store->nelem >= IMPL_CACHE_FLUSH_THRESHOLD)
1bdbdaff 583 store->need_flush = 1;
bdbf2df2 584 goto end;
1bdbdaff 585 }
bdbf2df2 586 ossl_method_free(&p->method);
1bdbdaff 587 }
bdbf2df2
P
588err:
589 res = 0;
defd3ed8 590 OPENSSL_free(p);
bdbf2df2
P
591end:
592 ossl_property_unlock(store);
593 return res;
1bdbdaff 594}